/**
  * @param string $list_string
  * @dataProvider provide_lists
  */
 function test_single_list($list_string)
 {
     $list = new GM_Int_List($list_string);
     $this->assertEquals($list_string, $list->expanded(), 'Expanded list is different from source.');
     $this->assertNotEmpty($list->compressed(), 'Compressed list is empty.');
     $reverse_list = new GM_Int_List($list->compressed());
     $this->assertEquals($list_string, $reverse_list->expanded(), 'Reverse expanded list is different from original.');
     $this->assertEquals($list->compressed(), $reverse_list->compressed(), 'Compressed list is different from source.');
 }
 /**
  * The map template tag.
  *
  * Returns HTML for a Google map. Must use with echo in a template: echo GeoMashup::map();.
  *
  * @since 1.0
  * @link http://github.com/cyberhobo/wordpress-geo-mashup/wiki/Tag-Reference#map tag parameter documentation
  * @uses $_SERVER['QUERY_STRING'] The first global map on a page uses query string parameters like tag parameters.
  * @uses apply_filters() geo_mashup_static_map Modify a static map image generated by geo mashup.
  * @staticvar $map_number Used to index maps per request.
  *
  * @param string|array $atts Template tag parameters.
  * @return string The HTML for the requested map.
  */
 public static function map($atts = null)
 {
     global $wp_query, $in_comment_loop, $geo_mashup_options;
     static $map_number = 1;
     $atts = wp_parse_args($atts);
     $static = (bool) (!empty($atts['static']) and 'true' == $atts['static']);
     unset($atts['static']);
     if (empty($atts['lang'])) {
         if (function_exists('qtrans_getLanguage')) {
             // qTranslate integration
             $atts['lang'] = qtrans_getLanguage();
         } else {
             if (defined('ICL_LANGUAGE_CODE')) {
                 // WPML integration
                 $atts['lang'] = ICL_LANGUAGE_CODE;
             }
         }
     }
     $click_to_load_options = array('click_to_load', 'click_to_load_text');
     self::convert_map_attributes($atts);
     // Default query is for posts
     $object_name = isset($atts['object_name']) ? $atts['object_name'] : 'post';
     // Map content type isn't required, if empty we'll choose one
     $map_content = isset($atts['map_content']) ? $atts['map_content'] : null;
     // Find the ID and location of the container object if it exists
     if ('post' == $object_name and $wp_query->in_the_loop) {
         $context_object_id = $wp_query->post->ID;
     } else {
         if ('comment' == $object_name and $in_comment_loop) {
             $context_object_id = get_comment_ID();
         } else {
             if ('user' == $object_name and $wp_query->post) {
                 $context_object_id = $wp_query->post->post_author;
             }
         }
     }
     if (empty($atts['object_id'])) {
         if (!empty($context_object_id)) {
             // If we found a context object, we'll query for that by default
             $atts['object_id'] = $context_object_id;
             $context_location = GeoMashupDB::get_object_location($object_name, $context_object_id);
         } else {
             if ('single' == $map_content and 'post' == $object_name) {
                 // In secondary post loops we won't find a context object
                 // but can at least allow explicit single maps
                 $atts['object_id'] = get_the_ID();
             }
         }
     }
     if (empty($map_content) and !empty($atts['object_ids'])) {
         $map_content = 'global';
     }
     if (empty($map_content)) {
         if (empty($context_object_id)) {
             $map_content = 'contextual';
         } else {
             if (empty($context_location)) {
                 // Not located, go global
                 $map_content = 'global';
             } else {
                 // Located, go single
                 $map_content = 'single';
             }
         }
     } else {
         if ($map_content instanceof WP_Query) {
             // We've been given a post query, put its contents in a global map
             $atts['object_ids'] = implode(',', wp_list_pluck($map_content->posts, 'ID'));
             $map_content = 'global';
         }
     }
     switch ($map_content) {
         case 'contextual':
             $atts['map_content'] = 'contextual';
             $atts += $geo_mashup_options->get('context_map', $click_to_load_options);
             $object_ids = array();
             if ('comment' == $object_name) {
                 $context_objects = $wp_query->comments;
             } else {
                 $context_objects = $wp_query->posts;
             }
             if (!is_array($context_objects)) {
                 return '<!-- ' . __('Geo Mashup found no objects to map in this context', 'GeoMashup') . '-->';
             }
             foreach ($context_objects as $context_object) {
                 if ('post' == $object_name) {
                     $object_ids[] = $context_object->ID;
                 } else {
                     if ('user' == $object_name) {
                         $object_ids[] = $context_object->post_author;
                     } else {
                         if ('comment' == $object_name) {
                             $object_ids[] = $context_object->comment_ID;
                         }
                     }
                 }
             }
             $atts['object_ids'] = implode(',', $object_ids);
             break;
         case 'single':
             $atts['map_content'] = 'single';
             $atts += $geo_mashup_options->get('single_map', $click_to_load_options);
             if (empty($atts['object_id'])) {
                 return '<!-- ' . __('Geo Mashup found no current object to map', 'GeoMashup') . '-->';
             }
             $single_location = GeoMashupDB::get_object_location($object_name, $atts['object_id']);
             if (empty($single_location)) {
                 return '<!-- ' . __('Geo Mashup omitted a map for an object with no location', 'GeoMashup') . '-->';
             }
             break;
         case 'global':
             if (isset($_GET['template']) and 'full-post' == $_GET['template']) {
                 // Global maps tags in response to a full-post query can infinitely nest, prevent this
                 return '<!-- ' . __('Geo Mashup map omitted to avoid nesting maps', 'GeoMashup') . '-->';
             }
             $atts['map_content'] = 'global';
             // Global maps on a page will make use of query string arguments unless directed otherwise
             $ignore_url = false;
             if (isset($atts['ignore_url']) && 'true' == $atts['ignore_url']) {
                 $ignore_url = true;
                 unset($atts['ignore_url']);
             }
             if (isset($_SERVER['QUERY_STRING']) and !$ignore_url) {
                 $atts = wp_parse_args($_SERVER['QUERY_STRING'], $atts);
             }
             $atts += $geo_mashup_options->get('global_map', $click_to_load_options);
             // Don't query more than max_posts
             $max_posts = $geo_mashup_options->get('global', 'max_posts');
             if (empty($atts['limit']) and !empty($max_posts)) {
                 $atts['limit'] = $max_posts;
             }
             break;
         default:
             return '<div class="gm-map"><p>Unrecognized value for map_content: "' . $map_content . '".</p></div>';
     }
     $click_to_load = $atts['click_to_load'];
     unset($atts['click_to_load']);
     $click_to_load_text = $atts['click_to_load_text'];
     unset($atts['click_to_load_text']);
     if (!isset($atts['name'])) {
         $atts['name'] = 'gm-map-' . $map_number;
     }
     $map_data = self::build_map_data($atts);
     if (empty($map_data['object_data']['objects']) and !isset($map_data['load_empty_map'])) {
         return '<!-- ' . __('Geo Mashup omitted a map with no located objects found.', 'GeoMashup') . '-->';
     }
     unset($map_data['load_empty_map']);
     $width_units = '%' === substr($map_data['width'], -1) ? '%' : 'px';
     $width = intval($map_data['width']);
     $width_style = $width . $width_units;
     $height_units = '%' === substr($map_data['height'], -1) ? '%' : 'px';
     $height = intval($map_data['height']);
     $height_style = $height . $height_units;
     $map_image = '';
     if ($static) {
         // Static maps have a limit of 50 markers: http://code.google.com/apis/maps/documentation/staticmaps/#Markers
         $atts['limit'] = empty($atts['limit']) ? 50 : $atts['limit'];
         if (!empty($map_data['object_data']['objects'])) {
             $map_image = '<img src="http://maps.google.com/maps/api/staticmap?size=' . $width . 'x' . $height;
             if (count($map_data['object_data']['objects']) == 1) {
                 $map_image .= '&amp;center=' . $map_data['object_data']['objects'][0]['lat'] . ',' . $map_data['object_data']['objects'][0]['lng'];
             }
             $map_image .= '&amp;sensor=false&amp;zoom=' . $map_data['zoom'] . '&amp;markers=size:small|color:red';
             foreach ($map_data['object_data']['objects'] as $location) {
                 // TODO: Try to use the correct color for the category? Draw category lines?
                 $map_image .= '|' . $location['lat'] . ',' . $location['lng'];
             }
             $map_image .= '" alt="geo_mashup_map"';
             if ($click_to_load == 'true') {
                 $map_image .= '" title="' . $click_to_load_text . '"';
             }
             $map_image .= ' />';
             $map_image = apply_filters('geo_mashup_static_map', $map_image, $map_data, array('click_to_load' => $click_to_load, 'click_to_load_text' => $click_to_load_text));
         }
     }
     $atts_md5 = md5(serialize($atts));
     set_transient('gmm' . $atts_md5, $map_data, 20);
     $src_args = array('geo_mashup_content' => 'render-map', 'map_data_key' => $atts_md5);
     if (!empty($atts['lang'])) {
         $src_args['lang'] = $atts['lang'];
     }
     if (isset($atts['object_ids']) and strlen($atts['object_ids']) > 1800) {
         // Try to shorten the URL a bit
         if (!class_exists('GM_Int_list')) {
             include GEO_MASHUP_DIR_PATH . '/gm-int-list.php';
         }
         $id_list = new GM_Int_List($atts['object_ids']);
         $atts['oids'] = $id_list->compressed();
         unset($atts['object_ids']);
     }
     $iframe_src = self::build_home_url($src_args + $atts);
     $content = "";
     if ($click_to_load == 'true') {
         if (is_feed()) {
             $content .= "<a href=\"{$iframe_src}\">{$click_to_load_text}</a>";
         } else {
             self::$add_loader_script = true;
             $style = "height: {$height_style}; width: {$width_style}; background-color: #ddd;" . "background-image: url(" . GEO_MASHUP_URL_PATH . "/images/wp-gm-pale.png);" . "background-repeat: no-repeat;background-position:center; cursor: pointer;";
             $content = "<div class=\"gm-map\" style=\"{$style}\" " . "onclick=\"GeoMashupLoader.addMapFrame(this,'{$iframe_src}','{$height_style}','{$width_style}','{$map_data['name']}')\">";
             if ($static) {
                 // TODO: test whether click to load really works with a static map
                 $content .= $map_image . '</div>';
             } else {
                 $content .= "<p style=\"text-align:center;\">{$click_to_load_text}</p></div>";
             }
         }
     } else {
         if ($static) {
             $content = "<div class=\"gm-map\">{$map_image}</div>";
         } else {
             $content = "<div class=\"gm-map\"><iframe name=\"{$map_data['name']}\" src=\"{$iframe_src}\" " . "style=\"height: {$height_style}; width: {$width_style}; border: none; overflow: hidden;\"></iframe></div>";
         }
     }
     $map_number++;
     return apply_filters('geo_mashup_map_content', $content, $map_data);
 }