├── travel ├── language │ └── en-GB │ │ ├── en-GB.plg_fields_travel.sys.ini │ │ └── en-GB.plg_fields_travel.ini ├── travel.php ├── tmpl │ ├── subtemplates │ │ ├── address.php │ │ ├── map.php │ │ ├── flickr.php │ │ └── weather.php │ └── travel.php ├── media │ ├── css │ │ ├── weather.css │ │ ├── masonry.css │ │ └── magnific-popup.css │ └── js │ │ ├── imagesloaded.js │ │ ├── masonry.pkgd.min.js │ │ └── jquery.magnific-popup.js ├── layouts │ └── field │ │ ├── address.php │ │ ├── map.php │ │ ├── flickr.php │ │ └── weather.php ├── fields │ └── travel.php ├── travel.xml └── params │ └── travel.xml └── README.md /travel/language/en-GB/en-GB.plg_fields_travel.sys.ini: -------------------------------------------------------------------------------- 1 | PLG_FIELDS_TRAVEL="Travel Field" 2 | PLG_FIELDS_TRAVEL_DESC="Displays different Travel Information." 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plg_fields_travel 2 | Joomla! CustomField - Displays an address, weather, map or flickr gallery from userinput - which is an autocomplete field. 3 | 4 | ## DOCS WILL FOLLOW 5 | 6 | ## ToDo: 7 | - file extension package for the layouts (move them now manually to the layouts folder) 8 | - translations 9 | - optimize curl call (cache?) 10 | -------------------------------------------------------------------------------- /travel/travel.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') { 15 | return; 16 | } 17 | 18 | $address = $field->rawvalue; 19 | 20 | echo $address; 21 | -------------------------------------------------------------------------------- /travel/media/css/weather.css: -------------------------------------------------------------------------------- 1 | .weather-container { 2 | background: #efefef; 3 | margin:15px 0; 4 | } 5 | 6 | .weather-container h4, 7 | .weather-date { 8 | text-transform: uppercase; 9 | font-size:1.2em; 10 | letter-spacing: 2px; 11 | } 12 | 13 | .weather-time { 14 | font-weight:bold; 15 | } 16 | 17 | .weather-left, 18 | .weather-right { 19 | padding:15px; 20 | } 21 | 22 | .hours { 23 | font-size:0.9em; 24 | } 25 | 26 | .hours .row:nth-child(even) { 27 | background: #dbdbdb; 28 | } -------------------------------------------------------------------------------- /travel/layouts/field/address.php: -------------------------------------------------------------------------------- 1 | rawvalue; 19 | 20 | if (!$field->value || $field->value == '-1') { 21 | return; 22 | } 23 | 24 | echo $value; 25 | -------------------------------------------------------------------------------- /travel/tmpl/travel.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') 15 | { 16 | return; 17 | } 18 | 19 | /* Prepare for plugin display */ 20 | JHTML::_('content.prepare', $value); 21 | 22 | /* Call the parameter that defines which template should be loaded */ 23 | $templates = $field->fieldparams['loadtemplates']; 24 | 25 | /* if a template is defined and the automatic display is ON */ 26 | if (is_array($templates) || is_object($templates) && $field->fieldparams['display'] == '1') 27 | { 28 | /* load the templates */ 29 | foreach ($templates as $template) 30 | { 31 | $tmplfile = 'subtemplates/' . $template . '.php'; 32 | include $tmplfile; 33 | } 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /travel/media/css/masonry.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | /* CSS Documentu */ 3 | 4 | .masonry-item { 5 | float: left; 6 | position: relative; 7 | line-height: 1em; 8 | } 9 | 10 | .grid-sizer { width: 24%; } 11 | .gutter-sizer { width: 1%; } 12 | .masonry-item { width: 24%; } 13 | .attribution {background: #efefef; text-align: center; padding:10px; width:100%; margin-bottom:10px; display:block;} 14 | 15 | @media screen and (max-width: 1224px) { 16 | /* 10 columns for larger screens */ 17 | .grid-sizer { width: 33.33%; } 18 | .masonry-item { width: 33.33%; } 19 | } 20 | 21 | @media screen and (max-width: 720px) { 22 | /* 10 columns for larger screens */ 23 | .grid-sizer { width: 50%; } 24 | .masonry-item { width: 50%; } 25 | } 26 | 27 | @media screen and (max-width: 480px) { 28 | /* 10 columns for larger screens */ 29 | .grid-sizer { width: 100%; } 30 | .masonry-item { width: 100%; } 31 | } 32 | 33 | .masonry-item img { 34 | max-width: 100%; 35 | margin: 0; 36 | display: block; 37 | } 38 | 39 | .masonry-item img:after { 40 | clear:both; 41 | } -------------------------------------------------------------------------------- /travel/fields/travel.php: -------------------------------------------------------------------------------- 1 | addScript('https://cdn.jsdelivr.net/npm/places.js@1.4.15', array(), array('defer' => 'defer')); 24 | $doc->addScriptDeclaration('document.addEventListener("DOMContentLoaded", function(event) { 25 | var placesAutocomplete = places({ 26 | container: document.querySelector(".address-input") 27 | }); 28 | });'); 29 | 30 | $placeholder = JText::_('PLG_FIELDS_TRAVEL_DESTINATION'); 31 | 32 | $field = ''; 33 | 34 | return $field; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /travel/language/en-GB/en-GB.plg_fields_travel.ini: -------------------------------------------------------------------------------- 1 | PLG_FIELDS_TRAVEL="Travel Field" 2 | PLG_FIELDS_TRAVEL_DESC="Displays different Travel Information." 3 | PLG_FIELDS_TRAVEL_DESTINATION="Where are we going?" 4 | PLG_TRAVEL_FLICKROPTIONS="Flickr Options" 5 | PLG_TRAVEL_WEATHEROPTIONS="Weather Options" 6 | PLG_TRAVEL_WEATHER_DEGREES="Degrees:" 7 | PLG_TRAVEL_WEATHER_WIND="Wind:" 8 | PLG_TRAVEL_WEATHER_AVERAGE="Average Temperature:" 9 | PLG_TRAVEL_WEATHER_MINMAX="Minimum and Maximum Temperature" 10 | PLG_TRAVEL_WEATHER_SUNRISE="Sunrise" 11 | PLG_TRAVEL_WEATHER_SUNSET="Sunset" 12 | PLG_TRAVEL_WEATHER_FORECAST="Weather Forecast for " 13 | PLG_TRAVEL_DATEFORMAT="Date Format" 14 | PLG_TRAVEL_WEATHER_HOURS_OVERVIEW="Weather per hour" 15 | PLG_TRAVEL_WEATHER_HUMIDITY="Humidity:" 16 | PLG_TRAVEL_WEATHER_TEMPERATURE="Temperature:" 17 | PLG_TRAVEL_WEATHER_TIME="" 18 | PLG_TRAVEL_WEATHER_BETWEEN="Between" 19 | PLG_TRAVEL_WEATHER_AND="and" 20 | PLG_TRAVEL_MAPOPTIONS="Map Options" 21 | PLG_TRAVEL_DEFINE_FLICKRKEY="Please provide a Flickr API Key in the configuration" 22 | PLG_TRAVEL_DEFINE_APIXUKEY="Please provide a Apixu API Key in the configuration" 23 | PLG_TRAVEL_WEATHER_PER_HOUR="Show Weather per Hour" -------------------------------------------------------------------------------- /travel/travel.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | plg_fields_travel 4 | Elisa Foltyn 5 | 2017 6 | www.coolcat-creations.com 7 | GNU General Public License version 2 or later; see LICENSE.txt 8 | contact@coolcat-creations.com 9 | www.coolcat-creations.com 10 | 1.0.0-alpha 11 | PLG_FIELDS_TRAVEL_DESC 12 | 13 | 14 | travel.php 15 | fields 16 | language 17 | params 18 | tmpl 19 | layouts 20 | 21 | 22 | css 23 | js 24 | 25 | 26 | 27 | en-GB.plg_fields_travel.ini 28 | en-GB.plg_fields_travel.sys.ini 29 | 30 | 31 | 32 | 33 |
34 | 40 | 46 | 52 |
53 |
54 |
55 | 56 |
57 | -------------------------------------------------------------------------------- /travel/layouts/field/map.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') 15 | { 16 | return; 17 | } 18 | 19 | /* Map Height */ 20 | $mapheight = $field->fieldparams['mapheight']; 21 | $plugin = JPluginHelper::getPlugin('fields', 'travel'); 22 | $pluginparams = json_decode($plugin->params); 23 | $key = $pluginparams->opencageapi; 24 | 25 | /* get the document */ 26 | $doc = JFactory::getDocument(); 27 | 28 | /* get the style for the leaflet map */ 29 | $doc->addStyleSheet('https://cdn.jsdelivr.net/leaflet/1/leaflet.css'); 30 | $doc->addStyleDeclaration('#map {height: ' . $mapheight . 'px; }'); 31 | 32 | 33 | /* get the script for the leaflet map */ 34 | $doc->addScript('https://cdn.jsdelivr.net/leaflet/1/leaflet.js'); 35 | 36 | 37 | /* Fill in the gaps in adress to make a proper call in the url */ 38 | // $address = str_replace(" ", "%20", $field->value); 39 | $address = JFilterOutput::stringURLSafe($field->rawvalue); 40 | 41 | /* setting up the call url */ 42 | $url = "https://api.opencagedata.com/geocode/v1/json?q=$address&key=$key&pretty=1"; 43 | 44 | /* Getting the JSON DATA TO GET LAT & LONG */ 45 | 46 | $ch = curl_init($url); 47 | curl_setopt($ch, CURLOPT_TIMEOUT, 5); 48 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 49 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 50 | 51 | $curl_response = curl_exec($ch); 52 | $jsonobj = json_decode($curl_response); 53 | 54 | $lat = $jsonobj->results[0]->geometry->lat; 55 | $lon = $jsonobj->results[0]->geometry->lng; 56 | 57 | ?> 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 89 | -------------------------------------------------------------------------------- /travel/tmpl/subtemplates/map.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') 15 | { 16 | return; 17 | } 18 | 19 | /* Map Height */ 20 | $mapheight = $field->fieldparams['mapheight']; 21 | $plugin = JPluginHelper::getPlugin('fields', 'travel'); 22 | $pluginparams = json_decode($plugin->params); 23 | $key = $pluginparams->opencageapi; 24 | 25 | /* get the document */ 26 | $doc = JFactory::getDocument(); 27 | 28 | /* get the style for the leaflet map */ 29 | $doc->addStyleSheet('https://cdn.jsdelivr.net/leaflet/1/leaflet.css'); 30 | $doc->addStyleDeclaration('#map {height: ' . $mapheight . 'px; }'); 31 | 32 | 33 | /* get the script for the leaflet map */ 34 | $doc->addScript('https://cdn.jsdelivr.net/leaflet/1/leaflet.js'); 35 | 36 | 37 | /* Fill in the gaps in adress to make a proper call in the url */ 38 | // $address = str_replace(" ", "%20", $field->value); 39 | $address = JFilterOutput::stringURLSafe($field->rawvalue); 40 | 41 | /* setting up the call url */ 42 | $url = "https://api.opencagedata.com/geocode/v1/json?q=$address&key=$key&pretty=1"; 43 | 44 | /* Getting the JSON DATA TO GET LAT & LONG */ 45 | 46 | $ch = curl_init($url); 47 | curl_setopt($ch, CURLOPT_TIMEOUT, 5); 48 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); 49 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 50 | 51 | $curl_response = curl_exec($ch); 52 | $jsonobj = json_decode($curl_response); 53 | 54 | $lat = $jsonobj->results[0]->geometry->lat; 55 | $lon = $jsonobj->results[0]->geometry->lng; 56 | 57 | ?> 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 89 | -------------------------------------------------------------------------------- /travel/params/travel.xml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 37 | 38 | 44 | 45 | 46 | 57 | 58 | 65 | 66 | 67 | 68 | 69 | 76 | 77 | 78 | 79 | 80 | 87 | 88 | 89 | 90 | 91 | 98 | 99 | 100 | 101 | 102 | 103 | 109 | 110 | 120 | 121 |
122 |
123 |
124 | -------------------------------------------------------------------------------- /travel/layouts/field/flickr.php: -------------------------------------------------------------------------------- 1 | params); 20 | 21 | /*making sure jQuery is loaded first */ 22 | JHtml::_('jQuery.Framework'); 23 | 24 | /* get the document */ 25 | $doc = JFactory::getDocument(); 26 | 27 | /* load the style for the masonry effect */ 28 | $doc->addStyleSheet('media/plg_fields_travel/css/masonry.css'); 29 | 30 | /* load the style for lightbox */ 31 | $doc->addStyleSheet('media/plg_fields_travel/css/magnific-popup.css'); 32 | 33 | /* load the script for the masonry effect */ 34 | $doc->addScript('media/plg_fields_travel/js/masonry.pkgd.min.js',array(),array('async'=>'async')); 35 | 36 | /* load the imageloaded script for the masonry effect */ 37 | $doc->addScript('media/plg_fields_travel/js/imagesloaded.js',array(),array('async'=>'async')); 38 | 39 | /* load the script for the lightbox */ 40 | $doc->addScript('media/plg_fields_travel/js/jquery.magnific-popup.js'); 41 | 42 | 43 | /* Fill in the gaps in adress to make a proper call in the url */ 44 | $address = JFilterOutput::stringURLSafe($field->rawvalue); 45 | 46 | /* How many images should be loaded */ 47 | $imgnum = $field->fieldparams['imgnum']; 48 | 49 | 50 | /* get the API Key that is set in the global params */ 51 | $key = $pluginparams->flickrapi; 52 | 53 | /* setting up the call url */ 54 | $url = "https://api.flickr.com/services/rest/?format=json&api_key=$key&method=flickr.photos.search&sort=relevance&text=$address&nojsoncallback=1&per_page=$imgnum&media=photos&content_type=1&extras=url_l,tags,owner_name,description"; 55 | 56 | 57 | /* Getting the JSON DATA */ 58 | $ch = curl_init(); 59 | curl_setopt($ch,CURLOPT_URL,$url); 60 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); 61 | 62 | $json_output=curl_exec($ch); 63 | $images = json_decode($json_output); 64 | 65 | $images = $images->photos->photo; 66 | 67 | ?> 68 | 69 | 70 |
71 | 72 | 73 |
74 | 75 | 76 |
77 | 78 | '; 83 | echo ''; 84 | echo ''; 85 | echo ''; 86 | echo '' . $image->ownername .''; 87 | echo '
'; 88 | 89 | } 90 | 91 | ?> 92 |
93 | 94 | 95 | 96 | 97 | 98 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /travel/tmpl/subtemplates/flickr.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') { 15 | return; 16 | } 17 | 18 | /*making sure jQuery is loaded first */ 19 | JHtml::_('jQuery.Framework'); 20 | 21 | /* get the document */ 22 | $doc = JFactory::getDocument(); 23 | 24 | /* load the style for the masonry effect */ 25 | $doc->addStyleSheet('media/plg_fields_travel/css/masonry.css'); 26 | 27 | /* load the style for lightbox */ 28 | $doc->addStyleSheet('media/plg_fields_travel/css/magnific-popup.css'); 29 | 30 | /* load the script for the masonry effect */ 31 | $doc->addScript('media/plg_fields_travel/js/masonry.pkgd.min.js',array(),array('async'=>'async')); 32 | 33 | /* load the script for the lightbox */ 34 | $doc->addScript('media/plg_fields_travel/js/jquery.magnific-popup.js'); 35 | 36 | /* load the imageloaded script for the masonry effect */ 37 | $doc->addScript('media/plg_fields_travel/js/imagesloaded.js',array(),array('async'=>'async')); 38 | 39 | 40 | /* Fill in the gaps in adress to make a proper call in the url */ 41 | $address = JFilterOutput::stringURLSafe($field->value); 42 | 43 | /* How many images should be loaded */ 44 | $imgnum = $field->fieldparams['imgnum']; 45 | 46 | 47 | /* get the API Key that is set in the global params */ 48 | $key = $this->params['flickrapi']; 49 | 50 | if (!$key) 51 | { 52 | JFactory::getApplication()->enqueueMessage(JText::_('PLG_TRAVEL_DEFINE_FLICKRKEY'), 'error'); 53 | return; 54 | } 55 | 56 | /* Find out the place id */ 57 | $placeurl = "https://api.flickr.com/services/rest/?method=flickr.places.find&api_key=$key&query=$address&format=json&nojsoncallback=1"; 58 | 59 | /* Getting the JSON DATA for Place */ 60 | $placecall = curl_init(); 61 | curl_setopt($placecall,CURLOPT_URL,$placeurl); 62 | curl_setopt($placecall,CURLOPT_RETURNTRANSFER,true); 63 | 64 | $json_output_place = curl_exec($placecall); 65 | 66 | $place = json_decode($json_output_place); 67 | 68 | $place_id = $place->places->place[0]->place_id; 69 | 70 | /* setting up the call url */ 71 | $url = "https://api.flickr.com/services/rest/?format=json&api_key=$key&method=flickr.photos.search&sort=interestingness-desc&nojsoncallback=1&per_page=$imgnum&media=photos&place_id=$place_id&accuracy=10&content_type=1&extras=url_l,tags,owner_name,description"; 72 | 73 | /* Getting the JSON DATA */ 74 | $ch = curl_init(); 75 | curl_setopt($ch,CURLOPT_URL,$url); 76 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); 77 | 78 | $json_output=curl_exec($ch); 79 | 80 | $images = json_decode($json_output); 81 | 82 | $images = $images->photos->photo; 83 | 84 | ?> 85 | 86 | 87 |
88 | 89 | 90 |
91 | 92 | 93 |
94 | 95 | '; 100 | echo ''; 101 | echo ''; 102 | echo ''; 103 | echo '' . $image->ownername .''; 104 | echo '
'; 105 | 106 | } 107 | 108 | ?> 109 |
110 | 111 | 112 | 113 | 114 | 115 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /travel/tmpl/subtemplates/weather.php: -------------------------------------------------------------------------------- 1 | rawvalue; 13 | 14 | if (!$field->value || $field->value == '-1') 15 | { 16 | return; 17 | } 18 | 19 | /* get the document */ 20 | $doc = JFactory::getDocument(); 21 | 22 | /* load the style for the weather layout */ 23 | $doc->addStyleSheet('media/plg_fields_travel/css/weather.css'); 24 | 25 | /* get the API Key that is set in the global params */ 26 | $key = $this->params['apixuapi']; 27 | 28 | if (!$key) 29 | { 30 | JFactory::getApplication()->enqueueMessage(JText::_('PLG_TRAVEL_DEFINE_APIXUKEY'), 'error'); 31 | 32 | return; 33 | } 34 | 35 | /* How many images should be loaded */ 36 | $forecast_days = $field->fieldparams['forecastdays']; 37 | 38 | /* Fahrenheit or Celcius */ 39 | $degrees = $field->fieldparams['degrees']; 40 | 41 | /* Show weather per hour or not */ 42 | $perhour = $field->fieldparams['perhour']; 43 | 44 | /* Dateformat */ 45 | $dateformat = $field->fieldparams['dateformat']; 46 | 47 | /* Miles or Kilometer */ 48 | $wind = $field->fieldparams['wind']; 49 | 50 | /* Fill in the gaps in adress to make a proper call in the url */ 51 | $address = JFilterOutput::stringURLSafe($field->value); 52 | 53 | /* setting up the call url */ 54 | $url = "http://api.apixu.com/v1/forecast.json?key=$key&q=$address&days=$forecast_days&="; 55 | 56 | /* Getting the JSON DATA */ 57 | $ch = curl_init(); 58 | curl_setopt($ch, CURLOPT_URL, $url); 59 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 60 | 61 | $json_output = curl_exec($ch); 62 | $weather = json_decode($json_output); 63 | $days = $weather->forecast->forecastday; 64 | 65 | /* output the weather layout */ 66 | 67 | echo '

' . JTEXT::_('PLG_TRAVEL_WEATHER_FORECAST') . $value . '

'; ?> 68 | 69 | 87 | 88 |
89 | 90 | 93 | 94 |
98 | 99 | 100 |
101 |
102 |
103 |
104 | 105 | 106 |
107 | 108 | Weather in <?php echo $value; ?> 111 |

day->condition->text; ?>

112 | 113 | 114 | day->avgtemp_c . '°C' : $day->day->avgtemp_f . '°F'); ?> 115 |
116 |
117 | 118 | 119 | 120 | day->mintemp_c . '°C ' . JTEXT::_('PLG_TRAVEL_WEATHER_AND') . ' ' . $day->day->maxtemp_c . '°C' 122 | : $day->day->mintemp_f . '°F ' . JTEXT::_('PLG_TRAVEL_WEATHER_AND') . ' ' . $day->day->maxtemp_f . '°F'); ?> 123 | 124 |
125 | 126 |
127 |

128 | astro->sunrise; ?> 129 | / 130 | astro->sunset; ?> 131 | / 132 | day->maxwind_kph . 'kpH' : $day->day->maxwind_mph . 'mpH'); ?> 133 |

134 |

135 |

136 |
137 |
138 |
139 | 140 | 141 | 142 | 143 |
144 |
145 |

146 | 147 |
148 | hour as $hr) 149 | { 150 | ?> 151 |
152 |
153 | <?php echo $hr->time; ?> 155 |
156 |
157 | 158 | time)); ?>
159 | temp_c . '°C' : $hr->temp_f . '°F'); ?> 160 | / 161 | wind_kph . ' kpH' : $hr->wind_mph . ' mpH'); ?> 162 | / 163 | humidity; ?> 164 | % 165 |
166 | 167 |
168 | 169 | 173 |
174 |
175 |
176 | 177 | 178 | 179 |
180 |
181 |
182 | 183 | 185 | 186 |
187 | -------------------------------------------------------------------------------- /travel/layouts/field/weather.php: -------------------------------------------------------------------------------- 1 | rawvalue; 20 | $plugin = JPluginHelper::getPlugin('fields', 'travel'); 21 | $pluginparams = json_decode($plugin->params); 22 | 23 | /* get the document */ 24 | $doc = JFactory::getDocument(); 25 | 26 | /* load the style for the weather layout */ 27 | $doc->addStyleSheet('media/plg_fields_travel/css/weather.css'); 28 | 29 | /* get the API Key that is set in the global params */ 30 | $key = $pluginparams->apixuapi; 31 | 32 | /* How many images should be loaded */ 33 | $forecast_days = $field->fieldparams['forecastdays']; 34 | 35 | /* Fahrenheit or Celcius */ 36 | $degrees = $field->fieldparams['degrees']; 37 | 38 | /* Show weather per hour or not */ 39 | $perhour = $field->fieldparams['perhour']; 40 | 41 | /* Dateformat */ 42 | $dateformat = $field->fieldparams['dateformat']; 43 | 44 | /* Miles or Kilometer */ 45 | $wind = $field->fieldparams['wind']; 46 | 47 | /* Fill in the gaps in adress to make a proper call in the url */ 48 | $address = JFilterOutput::stringURLSafe($field->rawvalue); 49 | 50 | /* setting up the call url */ 51 | $url = "http://api.apixu.com/v1/forecast.json?key=$key&q=$address&days=$forecast_days&="; 52 | 53 | /* Getting the JSON DATA */ 54 | $ch = curl_init(); 55 | curl_setopt($ch, CURLOPT_URL, $url); 56 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 57 | 58 | $json_output = curl_exec($ch); 59 | $weather = json_decode($json_output); 60 | $days = $weather->forecast->forecastday; 61 | 62 | /* output the weather layout */ 63 | 64 | echo '

' . JTEXT::_('PLG_TRAVEL_WEATHER_FORECAST') . $value . '

'; ?> 65 | 66 | 84 | 85 |
86 | 87 | 90 | 91 |
95 | 96 | 97 |
98 |
99 |
100 |
101 | 102 | 103 |
104 | 105 | Weather in <?php echo $value; ?> 108 |

day->condition->text; ?>

109 | 110 | 111 | day->avgtemp_c . '°C' : $day->day->avgtemp_f . '°F'); ?> 112 |
113 |
114 | 115 | 116 | 117 | day->mintemp_c . '°C ' . JTEXT::_('PLG_TRAVEL_WEATHER_AND') . ' ' . $day->day->maxtemp_c . '°C' 119 | : $day->day->mintemp_f . '°F ' . JTEXT::_('PLG_TRAVEL_WEATHER_AND') . ' ' . $day->day->maxtemp_f . '°F'); ?> 120 | 121 |
122 | 123 |
124 |

125 | astro->sunrise; ?> 126 | / 127 | astro->sunset; ?> 128 | / 129 | day->maxwind_kph . 'kpH' : $day->day->maxwind_mph . 'mpH'); ?> 130 |

131 |

132 |

133 |
134 |
135 |
136 | 137 | 138 | 139 |
140 |
141 |

142 | 143 |
144 | hour as $hr) 145 | { 146 | ?> 147 |
148 |
149 | <?php echo $hr->time; ?> 151 |
152 |
153 | 154 | time)); ?>
155 | temp_c . '°C' : $hr->temp_f . '°F'); ?> 156 | / 157 | wind_kph . ' kpH' : $hr->wind_mph . ' mpH'); ?> 158 | / 159 | humidity; ?> 160 | % 161 |
162 | 163 |
164 | 165 | 169 |
170 |
171 |
172 |
173 | 174 |
175 |
176 | 177 | 179 | 180 |
181 | -------------------------------------------------------------------------------- /travel/media/css/magnific-popup.css: -------------------------------------------------------------------------------- 1 | /* Magnific Popup CSS */ 2 | .mfp-bg { 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | z-index: 1042; 8 | overflow: hidden; 9 | position: fixed; 10 | background: #0b0b0b; 11 | opacity: 0.8; } 12 | 13 | .mfp-wrap { 14 | top: 0; 15 | left: 0; 16 | width: 100%; 17 | height: 100%; 18 | z-index: 1043; 19 | position: fixed; 20 | outline: none !important; 21 | -webkit-backface-visibility: hidden; } 22 | 23 | .mfp-container { 24 | text-align: center; 25 | position: absolute; 26 | width: 100%; 27 | height: 100%; 28 | left: 0; 29 | top: 0; 30 | padding: 0 8px; 31 | box-sizing: border-box; } 32 | 33 | .mfp-container:before { 34 | content: ''; 35 | display: inline-block; 36 | height: 100%; 37 | vertical-align: middle; } 38 | 39 | .mfp-align-top .mfp-container:before { 40 | display: none; } 41 | 42 | .mfp-content { 43 | position: relative; 44 | display: inline-block; 45 | vertical-align: middle; 46 | margin: 0 auto; 47 | text-align: left; 48 | z-index: 1045; } 49 | 50 | .mfp-inline-holder .mfp-content, 51 | .mfp-ajax-holder .mfp-content { 52 | width: 100%; 53 | cursor: auto; } 54 | 55 | .mfp-ajax-cur { 56 | cursor: progress; } 57 | 58 | .mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close { 59 | cursor: -moz-zoom-out; 60 | cursor: -webkit-zoom-out; 61 | cursor: zoom-out; } 62 | 63 | .mfp-zoom { 64 | cursor: pointer; 65 | cursor: -webkit-zoom-in; 66 | cursor: -moz-zoom-in; 67 | cursor: zoom-in; } 68 | 69 | .mfp-auto-cursor .mfp-content { 70 | cursor: auto; } 71 | 72 | .mfp-close, 73 | .mfp-arrow, 74 | .mfp-preloader, 75 | .mfp-counter { 76 | -webkit-user-select: none; 77 | -moz-user-select: none; 78 | user-select: none; } 79 | 80 | .mfp-loading.mfp-figure { 81 | display: none; } 82 | 83 | .mfp-hide { 84 | display: none !important; } 85 | 86 | .mfp-preloader { 87 | color: #CCC; 88 | position: absolute; 89 | top: 50%; 90 | width: auto; 91 | text-align: center; 92 | margin-top: -0.8em; 93 | left: 8px; 94 | right: 8px; 95 | z-index: 1044; } 96 | .mfp-preloader a { 97 | color: #CCC; } 98 | .mfp-preloader a:hover { 99 | color: #FFF; } 100 | 101 | .mfp-s-ready .mfp-preloader { 102 | display: none; } 103 | 104 | .mfp-s-error .mfp-content { 105 | display: none; } 106 | 107 | button.mfp-close, 108 | button.mfp-arrow { 109 | overflow: visible; 110 | cursor: pointer; 111 | background: transparent; 112 | border: 0; 113 | -webkit-appearance: none; 114 | display: block; 115 | outline: none; 116 | padding: 0; 117 | z-index: 1046; 118 | box-shadow: none; 119 | touch-action: manipulation; } 120 | 121 | button::-moz-focus-inner { 122 | padding: 0; 123 | border: 0; } 124 | 125 | .mfp-close { 126 | width: 44px; 127 | height: 44px; 128 | line-height: 44px; 129 | position: absolute; 130 | right: 0; 131 | top: 0; 132 | text-decoration: none; 133 | text-align: center; 134 | opacity: 0.65; 135 | padding: 0 0 18px 10px; 136 | color: #FFF; 137 | font-style: normal; 138 | font-size: 28px; 139 | font-family: Arial, Baskerville, monospace; } 140 | .mfp-close:hover, 141 | .mfp-close:focus { 142 | opacity: 1; } 143 | .mfp-close:active { 144 | top: 1px; } 145 | 146 | .mfp-close-btn-in .mfp-close { 147 | color: #333; } 148 | 149 | .mfp-image-holder .mfp-close, 150 | .mfp-iframe-holder .mfp-close { 151 | color: #FFF; 152 | right: -6px; 153 | text-align: right; 154 | padding-right: 6px; 155 | width: 100%; } 156 | 157 | .mfp-counter { 158 | position: absolute; 159 | top: 0; 160 | right: 0; 161 | color: #CCC; 162 | font-size: 12px; 163 | line-height: 18px; 164 | white-space: nowrap; } 165 | 166 | .mfp-arrow { 167 | position: absolute; 168 | opacity: 0.65; 169 | margin: 0; 170 | top: 50%; 171 | margin-top: -55px; 172 | padding: 0; 173 | width: 90px; 174 | height: 110px; 175 | -webkit-tap-highlight-color: transparent; } 176 | .mfp-arrow:active { 177 | margin-top: -54px; } 178 | .mfp-arrow:hover, 179 | .mfp-arrow:focus { 180 | opacity: 1; } 181 | .mfp-arrow:before, 182 | .mfp-arrow:after { 183 | content: ''; 184 | display: block; 185 | width: 0; 186 | height: 0; 187 | position: absolute; 188 | left: 0; 189 | top: 0; 190 | margin-top: 35px; 191 | margin-left: 35px; 192 | border: medium inset transparent; } 193 | .mfp-arrow:after { 194 | border-top-width: 13px; 195 | border-bottom-width: 13px; 196 | top: 8px; } 197 | .mfp-arrow:before { 198 | border-top-width: 21px; 199 | border-bottom-width: 21px; 200 | opacity: 0.7; } 201 | 202 | .mfp-arrow-left { 203 | left: 0; } 204 | .mfp-arrow-left:after { 205 | border-right: 17px solid #FFF; 206 | margin-left: 31px; } 207 | .mfp-arrow-left:before { 208 | margin-left: 25px; 209 | border-right: 27px solid #3F3F3F; } 210 | 211 | .mfp-arrow-right { 212 | right: 0; } 213 | .mfp-arrow-right:after { 214 | border-left: 17px solid #FFF; 215 | margin-left: 39px; } 216 | .mfp-arrow-right:before { 217 | border-left: 27px solid #3F3F3F; } 218 | 219 | .mfp-iframe-holder { 220 | padding-top: 40px; 221 | padding-bottom: 40px; } 222 | .mfp-iframe-holder .mfp-content { 223 | line-height: 0; 224 | width: 100%; 225 | max-width: 900px; } 226 | .mfp-iframe-holder .mfp-close { 227 | top: -40px; } 228 | 229 | .mfp-iframe-scaler { 230 | width: 100%; 231 | height: 0; 232 | overflow: hidden; 233 | padding-top: 56.25%; } 234 | .mfp-iframe-scaler iframe { 235 | position: absolute; 236 | display: block; 237 | top: 0; 238 | left: 0; 239 | width: 100%; 240 | height: 100%; 241 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.6); 242 | background: #000; } 243 | 244 | /* Main image in popup */ 245 | img.mfp-img { 246 | width: auto; 247 | max-width: 100%; 248 | height: auto; 249 | display: block; 250 | line-height: 0; 251 | box-sizing: border-box; 252 | padding: 40px 0 40px; 253 | margin: 0 auto; } 254 | 255 | /* The shadow behind the image */ 256 | .mfp-figure { 257 | line-height: 0; } 258 | .mfp-figure:after { 259 | content: ''; 260 | position: absolute; 261 | left: 0; 262 | top: 40px; 263 | bottom: 40px; 264 | display: block; 265 | right: 0; 266 | width: auto; 267 | height: auto; 268 | z-index: -1; 269 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.6); 270 | background: #444; } 271 | .mfp-figure small { 272 | color: #BDBDBD; 273 | display: block; 274 | font-size: 12px; 275 | line-height: 14px; } 276 | .mfp-figure figure { 277 | margin: 0; } 278 | 279 | .mfp-bottom-bar { 280 | margin-top: -36px; 281 | position: absolute; 282 | top: 100%; 283 | left: 0; 284 | width: 100%; 285 | cursor: auto; } 286 | 287 | .mfp-title { 288 | text-align: left; 289 | line-height: 18px; 290 | color: #F3F3F3; 291 | word-wrap: break-word; 292 | padding-right: 36px; } 293 | 294 | .mfp-image-holder .mfp-content { 295 | max-width: 100%; } 296 | 297 | .mfp-gallery .mfp-image-holder .mfp-figure { 298 | cursor: pointer; } 299 | 300 | @media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) { 301 | /** 302 | * Remove all paddings around the image on small screen 303 | */ 304 | .mfp-img-mobile .mfp-image-holder { 305 | padding-left: 0; 306 | padding-right: 0; } 307 | .mfp-img-mobile img.mfp-img { 308 | padding: 0; } 309 | .mfp-img-mobile .mfp-figure:after { 310 | top: 0; 311 | bottom: 0; } 312 | .mfp-img-mobile .mfp-figure small { 313 | display: inline; 314 | margin-left: 5px; } 315 | .mfp-img-mobile .mfp-bottom-bar { 316 | background: rgba(0, 0, 0, 0.6); 317 | bottom: 0; 318 | margin: 0; 319 | top: auto; 320 | padding: 3px 5px; 321 | position: fixed; 322 | box-sizing: border-box; } 323 | .mfp-img-mobile .mfp-bottom-bar:empty { 324 | padding: 0; } 325 | .mfp-img-mobile .mfp-counter { 326 | right: 5px; 327 | top: 3px; } 328 | .mfp-img-mobile .mfp-close { 329 | top: 0; 330 | right: 0; 331 | width: 35px; 332 | height: 35px; 333 | line-height: 35px; 334 | background: rgba(0, 0, 0, 0.6); 335 | position: fixed; 336 | text-align: center; 337 | padding: 0; } } 338 | 339 | @media all and (max-width: 900px) { 340 | .mfp-arrow { 341 | -webkit-transform: scale(0.75); 342 | transform: scale(0.75); } 343 | .mfp-arrow-left { 344 | -webkit-transform-origin: 0; 345 | transform-origin: 0; } 346 | .mfp-arrow-right { 347 | -webkit-transform-origin: 100%; 348 | transform-origin: 100%; } 349 | .mfp-container { 350 | padding-left: 6px; 351 | padding-right: 6px; } } 352 | u -------------------------------------------------------------------------------- /travel/media/js/imagesloaded.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * imagesLoaded v4.1.3 3 | * JavaScript is all like "You images are done yet or what?" 4 | * MIT License 5 | */ 6 | 7 | ( function( window, factory ) { 'use strict'; 8 | // universal module definition 9 | 10 | /*global define: false, module: false, require: false */ 11 | 12 | if ( typeof define == 'function' && define.amd ) { 13 | // AMD 14 | define( [ 15 | 'ev-emitter/ev-emitter' 16 | ], function( EvEmitter ) { 17 | return factory( window, EvEmitter ); 18 | }); 19 | } else if ( typeof module == 'object' && module.exports ) { 20 | // CommonJS 21 | module.exports = factory( 22 | window, 23 | require('ev-emitter') 24 | ); 25 | } else { 26 | // browser global 27 | window.imagesLoaded = factory( 28 | window, 29 | window.EvEmitter 30 | ); 31 | } 32 | 33 | })( typeof window !== 'undefined' ? window : this, 34 | 35 | // -------------------------- factory -------------------------- // 36 | 37 | function factory( window, EvEmitter ) { 38 | 39 | 'use strict'; 40 | 41 | var $ = window.jQuery; 42 | var console = window.console; 43 | 44 | // -------------------------- helpers -------------------------- // 45 | 46 | // extend objects 47 | function extend( a, b ) { 48 | for ( var prop in b ) { 49 | a[ prop ] = b[ prop ]; 50 | } 51 | return a; 52 | } 53 | 54 | // turn element or nodeList into an array 55 | function makeArray( obj ) { 56 | var ary = []; 57 | if ( Array.isArray( obj ) ) { 58 | // use object if already an array 59 | ary = obj; 60 | } else if ( typeof obj.length == 'number' ) { 61 | // convert nodeList to array 62 | for ( var i=0; i < obj.length; i++ ) { 63 | ary.push( obj[i] ); 64 | } 65 | } else { 66 | // array of single index 67 | ary.push( obj ); 68 | } 69 | return ary; 70 | } 71 | 72 | // -------------------------- imagesLoaded -------------------------- // 73 | 74 | /** 75 | * @param {Array, Element, NodeList, String} elem 76 | * @param {Object or Function} options - if function, use as callback 77 | * @param {Function} onAlways - callback function 78 | */ 79 | function ImagesLoaded( elem, options, onAlways ) { 80 | // coerce ImagesLoaded() without new, to be new ImagesLoaded() 81 | if ( !( this instanceof ImagesLoaded ) ) { 82 | return new ImagesLoaded( elem, options, onAlways ); 83 | } 84 | // use elem as selector string 85 | if ( typeof elem == 'string' ) { 86 | elem = document.querySelectorAll( elem ); 87 | } 88 | 89 | this.elements = makeArray( elem ); 90 | this.options = extend( {}, this.options ); 91 | 92 | if ( typeof options == 'function' ) { 93 | onAlways = options; 94 | } else { 95 | extend( this.options, options ); 96 | } 97 | 98 | if ( onAlways ) { 99 | this.on( 'always', onAlways ); 100 | } 101 | 102 | this.getImages(); 103 | 104 | if ( $ ) { 105 | // add jQuery Deferred object 106 | this.jqDeferred = new $.Deferred(); 107 | } 108 | 109 | // HACK check async to allow time to bind listeners 110 | setTimeout( function() { 111 | this.check(); 112 | }.bind( this )); 113 | } 114 | 115 | ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); 116 | 117 | ImagesLoaded.prototype.options = {}; 118 | 119 | ImagesLoaded.prototype.getImages = function() { 120 | this.images = []; 121 | 122 | // filter & find items if we have an item selector 123 | this.elements.forEach( this.addElementImages, this ); 124 | }; 125 | 126 | /** 127 | * @param {Node} element 128 | */ 129 | ImagesLoaded.prototype.addElementImages = function( elem ) { 130 | // filter siblings 131 | if ( elem.nodeName == 'IMG' ) { 132 | this.addImage( elem ); 133 | } 134 | // get background image on element 135 | if ( this.options.background === true ) { 136 | this.addElementBackgroundImages( elem ); 137 | } 138 | 139 | // find children 140 | // no non-element nodes, #143 141 | var nodeType = elem.nodeType; 142 | if ( !nodeType || !elementNodeTypes[ nodeType ] ) { 143 | return; 144 | } 145 | var childImgs = elem.querySelectorAll('img'); 146 | // concat childElems to filterFound array 147 | for ( var i=0; i < childImgs.length; i++ ) { 148 | var img = childImgs[i]; 149 | this.addImage( img ); 150 | } 151 | 152 | // get child background images 153 | if ( typeof this.options.background == 'string' ) { 154 | var children = elem.querySelectorAll( this.options.background ); 155 | for ( i=0; i < children.length; i++ ) { 156 | var child = children[i]; 157 | this.addElementBackgroundImages( child ); 158 | } 159 | } 160 | }; 161 | 162 | var elementNodeTypes = { 163 | 1: true, 164 | 9: true, 165 | 11: true 166 | }; 167 | 168 | ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { 169 | var style = getComputedStyle( elem ); 170 | if ( !style ) { 171 | // Firefox returns null if in a hidden iframe https://bugzil.la/548397 172 | return; 173 | } 174 | // get url inside url("...") 175 | var reURL = /url\((['"])?(.*?)\1\)/gi; 176 | var matches = reURL.exec( style.backgroundImage ); 177 | while ( matches !== null ) { 178 | var url = matches && matches[2]; 179 | if ( url ) { 180 | this.addBackground( url, elem ); 181 | } 182 | matches = reURL.exec( style.backgroundImage ); 183 | } 184 | }; 185 | 186 | /** 187 | * @param {Image} img 188 | */ 189 | ImagesLoaded.prototype.addImage = function( img ) { 190 | var loadingImage = new LoadingImage( img ); 191 | this.images.push( loadingImage ); 192 | }; 193 | 194 | ImagesLoaded.prototype.addBackground = function( url, elem ) { 195 | var background = new Background( url, elem ); 196 | this.images.push( background ); 197 | }; 198 | 199 | ImagesLoaded.prototype.check = function() { 200 | var _this = this; 201 | this.progressedCount = 0; 202 | this.hasAnyBroken = false; 203 | // complete if no images 204 | if ( !this.images.length ) { 205 | this.complete(); 206 | return; 207 | } 208 | 209 | function onProgress( image, elem, message ) { 210 | // HACK - Chrome triggers event before object properties have changed. #83 211 | setTimeout( function() { 212 | _this.progress( image, elem, message ); 213 | }); 214 | } 215 | 216 | this.images.forEach( function( loadingImage ) { 217 | loadingImage.once( 'progress', onProgress ); 218 | loadingImage.check(); 219 | }); 220 | }; 221 | 222 | ImagesLoaded.prototype.progress = function( image, elem, message ) { 223 | this.progressedCount++; 224 | this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 225 | // progress event 226 | this.emitEvent( 'progress', [ this, image, elem ] ); 227 | if ( this.jqDeferred && this.jqDeferred.notify ) { 228 | this.jqDeferred.notify( this, image ); 229 | } 230 | // check if completed 231 | if ( this.progressedCount == this.images.length ) { 232 | this.complete(); 233 | } 234 | 235 | if ( this.options.debug && console ) { 236 | console.log( 'progress: ' + message, image, elem ); 237 | } 238 | }; 239 | 240 | ImagesLoaded.prototype.complete = function() { 241 | var eventName = this.hasAnyBroken ? 'fail' : 'done'; 242 | this.isComplete = true; 243 | this.emitEvent( eventName, [ this ] ); 244 | this.emitEvent( 'always', [ this ] ); 245 | if ( this.jqDeferred ) { 246 | var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 247 | this.jqDeferred[ jqMethod ]( this ); 248 | } 249 | }; 250 | 251 | // -------------------------- -------------------------- // 252 | 253 | function LoadingImage( img ) { 254 | this.img = img; 255 | } 256 | 257 | LoadingImage.prototype = Object.create( EvEmitter.prototype ); 258 | 259 | LoadingImage.prototype.check = function() { 260 | // If complete is true and browser supports natural sizes, 261 | // try to check for image status manually. 262 | var isComplete = this.getIsImageComplete(); 263 | if ( isComplete ) { 264 | // report based on naturalWidth 265 | this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 266 | return; 267 | } 268 | 269 | // If none of the checks above matched, simulate loading on detached element. 270 | this.proxyImage = new Image(); 271 | this.proxyImage.addEventListener( 'load', this ); 272 | this.proxyImage.addEventListener( 'error', this ); 273 | // bind to image as well for Firefox. #191 274 | this.img.addEventListener( 'load', this ); 275 | this.img.addEventListener( 'error', this ); 276 | this.proxyImage.src = this.img.src; 277 | }; 278 | 279 | LoadingImage.prototype.getIsImageComplete = function() { 280 | return this.img.complete && this.img.naturalWidth !== undefined; 281 | }; 282 | 283 | LoadingImage.prototype.confirm = function( isLoaded, message ) { 284 | this.isLoaded = isLoaded; 285 | this.emitEvent( 'progress', [ this, this.img, message ] ); 286 | }; 287 | 288 | // ----- events ----- // 289 | 290 | // trigger specified handler for event type 291 | LoadingImage.prototype.handleEvent = function( event ) { 292 | var method = 'on' + event.type; 293 | if ( this[ method ] ) { 294 | this[ method ]( event ); 295 | } 296 | }; 297 | 298 | LoadingImage.prototype.onload = function() { 299 | this.confirm( true, 'onload' ); 300 | this.unbindEvents(); 301 | }; 302 | 303 | LoadingImage.prototype.onerror = function() { 304 | this.confirm( false, 'onerror' ); 305 | this.unbindEvents(); 306 | }; 307 | 308 | LoadingImage.prototype.unbindEvents = function() { 309 | this.proxyImage.removeEventListener( 'load', this ); 310 | this.proxyImage.removeEventListener( 'error', this ); 311 | this.img.removeEventListener( 'load', this ); 312 | this.img.removeEventListener( 'error', this ); 313 | }; 314 | 315 | // -------------------------- Background -------------------------- // 316 | 317 | function Background( url, element ) { 318 | this.url = url; 319 | this.element = element; 320 | this.img = new Image(); 321 | } 322 | 323 | // inherit LoadingImage prototype 324 | Background.prototype = Object.create( LoadingImage.prototype ); 325 | 326 | Background.prototype.check = function() { 327 | this.img.addEventListener( 'load', this ); 328 | this.img.addEventListener( 'error', this ); 329 | this.img.src = this.url; 330 | // check if image is already complete 331 | var isComplete = this.getIsImageComplete(); 332 | if ( isComplete ) { 333 | this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 334 | this.unbindEvents(); 335 | } 336 | }; 337 | 338 | Background.prototype.unbindEvents = function() { 339 | this.img.removeEventListener( 'load', this ); 340 | this.img.removeEventListener( 'error', this ); 341 | }; 342 | 343 | Background.prototype.confirm = function( isLoaded, message ) { 344 | this.isLoaded = isLoaded; 345 | this.emitEvent( 'progress', [ this, this.element, message ] ); 346 | }; 347 | 348 | // -------------------------- jQuery -------------------------- // 349 | 350 | ImagesLoaded.makeJQueryPlugin = function( jQuery ) { 351 | jQuery = jQuery || window.jQuery; 352 | if ( !jQuery ) { 353 | return; 354 | } 355 | // set local variable 356 | $ = jQuery; 357 | // $().imagesLoaded() 358 | $.fn.imagesLoaded = function( options, callback ) { 359 | var instance = new ImagesLoaded( this, options, callback ); 360 | return instance.jqDeferred.promise( $(this) ); 361 | }; 362 | }; 363 | // try making plugin 364 | ImagesLoaded.makeJQueryPlugin(); 365 | 366 | // -------------------------- -------------------------- // 367 | 368 | return ImagesLoaded; 369 | 370 | }); -------------------------------------------------------------------------------- /travel/media/js/masonry.pkgd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Masonry PACKAGED v4.2.0 3 | * Cascading grid layout library 4 | * http://masonry.desandro.com 5 | * MIT License 6 | * by David DeSandro 7 | */ 8 | 9 | !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict";function t(t){var e=parseFloat(t),i=-1==t.indexOf("%")&&!isNaN(e);return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;u>e;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),e}function o(){if(!d){d=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);r.isBoxSizeOuter=s=200==t(o.width),i.removeChild(e)}}function r(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var r=n(e);if("none"==r.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var d=a.isBorderBox="border-box"==r.boxSizing,l=0;u>l;l++){var c=h[l],f=r[c],m=parseFloat(f);a[c]=isNaN(m)?0:m}var p=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,z=a.borderTopWidth+a.borderBottomWidth,E=d&&s,b=t(r.width);b!==!1&&(a.width=b+(E?0:p+_));var x=t(r.height);return x!==!1&&(a.height=x+(E?0:g+z)),a.innerWidth=a.width-(p+_),a.innerHeight=a.height-(g+z),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var s,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,d=!1;return r}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict";var t=function(){var t=window.Element.prototype;if(t.matches)return"matches";if(t.matchesSelector)return"matchesSelector";for(var e=["webkit","moz","ms","o"],i=0;is?"round":"floor";r=Math[a](r),this.cols=Math.max(r,1)},n.getContainerWidth=function(){var t=this._getOption("fitWidth"),i=t?this.element.parentNode:this.element,n=e(i);this.containerWidth=n&&n.innerWidth},n._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,i=e&&1>e?"round":"ceil",n=Math[i](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var o=this.options.horizontalOrder?"_getHorizontalColPosition":"_getTopColPosition",r=this[o](n,t),s={x:this.columnWidth*r.col,y:r.y},a=r.y+t.size.outerHeight,h=n+r.col,u=r.col;h>u;u++)this.colYs[u]=a;return s},n._getTopColPosition=function(t){var e=this._getTopColGroup(t),i=Math.min.apply(Math,e);return{col:e.indexOf(i),y:i}},n._getTopColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,n=0;i>n;n++)e[n]=this._getColGroupY(n,t);return e},n._getColGroupY=function(t,e){if(2>e)return this.colYs[t];var i=this.colYs.slice(t,t+e);return Math.max.apply(Math,i)},n._getHorizontalColPosition=function(t,e){var i=this.horizontalColIndex%this.cols,n=t>1&&i+t>this.cols;i=n?0:i;var o=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=o?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},n._manageStamp=function(t){var i=e(t),n=this._getElementOffset(t),o=this._getOption("originLeft"),r=o?n.left:n.right,s=r+i.outerWidth,a=Math.floor(r/this.columnWidth);a=Math.max(0,a);var h=Math.floor(s/this.columnWidth);h-=s%this.columnWidth?0:1,h=Math.min(this.cols-1,h);for(var u=this._getOption("originTop"),d=(u?n.top:n.bottom)+i.outerHeight,l=a;h>=l;l++)this.colYs[l]=Math.max(d,this.colYs[l])},n._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},n._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},n.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i}); -------------------------------------------------------------------------------- /travel/media/js/jquery.magnific-popup.js: -------------------------------------------------------------------------------- 1 | /*! Magnific Popup - v1.1.0 - 2016-02-20 2 | * http://dimsemenov.com/plugins/magnific-popup/ 3 | * Copyright (c) 2016 Dmitry Semenov; */ 4 | ;(function (factory) { 5 | if (typeof define === 'function' && define.amd) { 6 | // AMD. Register as an anonymous module. 7 | define(['jquery'], factory); 8 | } else if (typeof exports === 'object') { 9 | // Node/CommonJS 10 | factory(require('jquery')); 11 | } else { 12 | // Browser globals 13 | factory(window.jQuery || window.Zepto); 14 | } 15 | }(function($) { 16 | 17 | /*>>core*/ 18 | /** 19 | * 20 | * Magnific Popup Core JS file 21 | * 22 | */ 23 | 24 | 25 | /** 26 | * Private static constants 27 | */ 28 | var CLOSE_EVENT = 'Close', 29 | BEFORE_CLOSE_EVENT = 'BeforeClose', 30 | AFTER_CLOSE_EVENT = 'AfterClose', 31 | BEFORE_APPEND_EVENT = 'BeforeAppend', 32 | MARKUP_PARSE_EVENT = 'MarkupParse', 33 | OPEN_EVENT = 'Open', 34 | CHANGE_EVENT = 'Change', 35 | NS = 'mfp', 36 | EVENT_NS = '.' + NS, 37 | READY_CLASS = 'mfp-ready', 38 | REMOVING_CLASS = 'mfp-removing', 39 | PREVENT_CLOSE_CLASS = 'mfp-prevent-close'; 40 | 41 | 42 | /** 43 | * Private vars 44 | */ 45 | /*jshint -W079 */ 46 | var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this' 47 | MagnificPopup = function(){}, 48 | _isJQ = !!(window.jQuery), 49 | _prevStatus, 50 | _window = $(window), 51 | _document, 52 | _prevContentType, 53 | _wrapClasses, 54 | _currPopupType; 55 | 56 | 57 | /** 58 | * Private functions 59 | */ 60 | var _mfpOn = function(name, f) { 61 | mfp.ev.on(NS + name + EVENT_NS, f); 62 | }, 63 | _getEl = function(className, appendTo, html, raw) { 64 | var el = document.createElement('div'); 65 | el.className = 'mfp-'+className; 66 | if(html) { 67 | el.innerHTML = html; 68 | } 69 | if(!raw) { 70 | el = $(el); 71 | if(appendTo) { 72 | el.appendTo(appendTo); 73 | } 74 | } else if(appendTo) { 75 | appendTo.appendChild(el); 76 | } 77 | return el; 78 | }, 79 | _mfpTrigger = function(e, data) { 80 | mfp.ev.triggerHandler(NS + e, data); 81 | 82 | if(mfp.st.callbacks) { 83 | // converts "mfpEventName" to "eventName" callback and triggers it if it's present 84 | e = e.charAt(0).toLowerCase() + e.slice(1); 85 | if(mfp.st.callbacks[e]) { 86 | mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]); 87 | } 88 | } 89 | }, 90 | _getCloseBtn = function(type) { 91 | if(type !== _currPopupType || !mfp.currTemplate.closeBtn) { 92 | mfp.currTemplate.closeBtn = $( mfp.st.closeMarkup.replace('%title%', mfp.st.tClose ) ); 93 | _currPopupType = type; 94 | } 95 | return mfp.currTemplate.closeBtn; 96 | }, 97 | // Initialize Magnific Popup only when called at least once 98 | _checkInstance = function() { 99 | if(!$.magnificPopup.instance) { 100 | /*jshint -W020 */ 101 | mfp = new MagnificPopup(); 102 | mfp.init(); 103 | $.magnificPopup.instance = mfp; 104 | } 105 | }, 106 | // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr 107 | supportsTransitions = function() { 108 | var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist 109 | v = ['ms','O','Moz','Webkit']; // 'v' for vendor 110 | 111 | if( s['transition'] !== undefined ) { 112 | return true; 113 | } 114 | 115 | while( v.length ) { 116 | if( v.pop() + 'Transition' in s ) { 117 | return true; 118 | } 119 | } 120 | 121 | return false; 122 | }; 123 | 124 | 125 | 126 | /** 127 | * Public functions 128 | */ 129 | MagnificPopup.prototype = { 130 | 131 | constructor: MagnificPopup, 132 | 133 | /** 134 | * Initializes Magnific Popup plugin. 135 | * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed 136 | */ 137 | init: function() { 138 | var appVersion = navigator.appVersion; 139 | mfp.isLowIE = mfp.isIE8 = document.all && !document.addEventListener; 140 | mfp.isAndroid = (/android/gi).test(appVersion); 141 | mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion); 142 | mfp.supportsTransition = supportsTransitions(); 143 | 144 | // We disable fixed positioned lightbox on devices that don't handle it nicely. 145 | // If you know a better way of detecting this - let me know. 146 | mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent) ); 147 | _document = $(document); 148 | 149 | mfp.popupsCache = {}; 150 | }, 151 | 152 | /** 153 | * Opens popup 154 | * @param data [description] 155 | */ 156 | open: function(data) { 157 | 158 | var i; 159 | 160 | if(data.isObj === false) { 161 | // convert jQuery collection to array to avoid conflicts later 162 | mfp.items = data.items.toArray(); 163 | 164 | mfp.index = 0; 165 | var items = data.items, 166 | item; 167 | for(i = 0; i < items.length; i++) { 168 | item = items[i]; 169 | if(item.parsed) { 170 | item = item.el[0]; 171 | } 172 | if(item === data.el[0]) { 173 | mfp.index = i; 174 | break; 175 | } 176 | } 177 | } else { 178 | mfp.items = $.isArray(data.items) ? data.items : [data.items]; 179 | mfp.index = data.index || 0; 180 | } 181 | 182 | // if popup is already opened - we just update the content 183 | if(mfp.isOpen) { 184 | mfp.updateItemHTML(); 185 | return; 186 | } 187 | 188 | mfp.types = []; 189 | _wrapClasses = ''; 190 | if(data.mainEl && data.mainEl.length) { 191 | mfp.ev = data.mainEl.eq(0); 192 | } else { 193 | mfp.ev = _document; 194 | } 195 | 196 | if(data.key) { 197 | if(!mfp.popupsCache[data.key]) { 198 | mfp.popupsCache[data.key] = {}; 199 | } 200 | mfp.currTemplate = mfp.popupsCache[data.key]; 201 | } else { 202 | mfp.currTemplate = {}; 203 | } 204 | 205 | 206 | 207 | mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data ); 208 | mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos; 209 | 210 | if(mfp.st.modal) { 211 | mfp.st.closeOnContentClick = false; 212 | mfp.st.closeOnBgClick = false; 213 | mfp.st.showCloseBtn = false; 214 | mfp.st.enableEscapeKey = false; 215 | } 216 | 217 | 218 | // Building markup 219 | // main containers are created only once 220 | if(!mfp.bgOverlay) { 221 | 222 | // Dark overlay 223 | mfp.bgOverlay = _getEl('bg').on('click'+EVENT_NS, function() { 224 | mfp.close(); 225 | }); 226 | 227 | mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click'+EVENT_NS, function(e) { 228 | if(mfp._checkIfClose(e.target)) { 229 | mfp.close(); 230 | } 231 | }); 232 | 233 | mfp.container = _getEl('container', mfp.wrap); 234 | } 235 | 236 | mfp.contentContainer = _getEl('content'); 237 | if(mfp.st.preloader) { 238 | mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading); 239 | } 240 | 241 | 242 | // Initializing modules 243 | var modules = $.magnificPopup.modules; 244 | for(i = 0; i < modules.length; i++) { 245 | var n = modules[i]; 246 | n = n.charAt(0).toUpperCase() + n.slice(1); 247 | mfp['init'+n].call(mfp); 248 | } 249 | _mfpTrigger('BeforeOpen'); 250 | 251 | 252 | if(mfp.st.showCloseBtn) { 253 | // Close button 254 | if(!mfp.st.closeBtnInside) { 255 | mfp.wrap.append( _getCloseBtn() ); 256 | } else { 257 | _mfpOn(MARKUP_PARSE_EVENT, function(e, template, values, item) { 258 | values.close_replaceWith = _getCloseBtn(item.type); 259 | }); 260 | _wrapClasses += ' mfp-close-btn-in'; 261 | } 262 | } 263 | 264 | if(mfp.st.alignTop) { 265 | _wrapClasses += ' mfp-align-top'; 266 | } 267 | 268 | 269 | 270 | if(mfp.fixedContentPos) { 271 | mfp.wrap.css({ 272 | overflow: mfp.st.overflowY, 273 | overflowX: 'hidden', 274 | overflowY: mfp.st.overflowY 275 | }); 276 | } else { 277 | mfp.wrap.css({ 278 | top: _window.scrollTop(), 279 | position: 'absolute' 280 | }); 281 | } 282 | if( mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos) ) { 283 | mfp.bgOverlay.css({ 284 | height: _document.height(), 285 | position: 'absolute' 286 | }); 287 | } 288 | 289 | 290 | 291 | if(mfp.st.enableEscapeKey) { 292 | // Close on ESC key 293 | _document.on('keyup' + EVENT_NS, function(e) { 294 | if(e.keyCode === 27) { 295 | mfp.close(); 296 | } 297 | }); 298 | } 299 | 300 | _window.on('resize' + EVENT_NS, function() { 301 | mfp.updateSize(); 302 | }); 303 | 304 | 305 | if(!mfp.st.closeOnContentClick) { 306 | _wrapClasses += ' mfp-auto-cursor'; 307 | } 308 | 309 | if(_wrapClasses) 310 | mfp.wrap.addClass(_wrapClasses); 311 | 312 | 313 | // this triggers recalculation of layout, so we get it once to not to trigger twice 314 | var windowHeight = mfp.wH = _window.height(); 315 | 316 | 317 | var windowStyles = {}; 318 | 319 | if( mfp.fixedContentPos ) { 320 | if(mfp._hasScrollBar(windowHeight)){ 321 | var s = mfp._getScrollbarSize(); 322 | if(s) { 323 | windowStyles.marginRight = s; 324 | } 325 | } 326 | } 327 | 328 | if(mfp.fixedContentPos) { 329 | if(!mfp.isIE7) { 330 | windowStyles.overflow = 'hidden'; 331 | } else { 332 | // ie7 double-scroll bug 333 | $('body, html').css('overflow', 'hidden'); 334 | } 335 | } 336 | 337 | 338 | 339 | var classesToadd = mfp.st.mainClass; 340 | if(mfp.isIE7) { 341 | classesToadd += ' mfp-ie7'; 342 | } 343 | if(classesToadd) { 344 | mfp._addClassToMFP( classesToadd ); 345 | } 346 | 347 | // add content 348 | mfp.updateItemHTML(); 349 | 350 | _mfpTrigger('BuildControls'); 351 | 352 | // remove scrollbar, add margin e.t.c 353 | $('html').css(windowStyles); 354 | 355 | // add everything to DOM 356 | mfp.bgOverlay.add(mfp.wrap).prependTo( mfp.st.prependTo || $(document.body) ); 357 | 358 | // Save last focused element 359 | mfp._lastFocusedEl = document.activeElement; 360 | 361 | // Wait for next cycle to allow CSS transition 362 | setTimeout(function() { 363 | 364 | if(mfp.content) { 365 | mfp._addClassToMFP(READY_CLASS); 366 | mfp._setFocus(); 367 | } else { 368 | // if content is not defined (not loaded e.t.c) we add class only for BG 369 | mfp.bgOverlay.addClass(READY_CLASS); 370 | } 371 | 372 | // Trap the focus in popup 373 | _document.on('focusin' + EVENT_NS, mfp._onFocusIn); 374 | 375 | }, 16); 376 | 377 | mfp.isOpen = true; 378 | mfp.updateSize(windowHeight); 379 | _mfpTrigger(OPEN_EVENT); 380 | 381 | return data; 382 | }, 383 | 384 | /** 385 | * Closes the popup 386 | */ 387 | close: function() { 388 | if(!mfp.isOpen) return; 389 | _mfpTrigger(BEFORE_CLOSE_EVENT); 390 | 391 | mfp.isOpen = false; 392 | // for CSS3 animation 393 | if(mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition ) { 394 | mfp._addClassToMFP(REMOVING_CLASS); 395 | setTimeout(function() { 396 | mfp._close(); 397 | }, mfp.st.removalDelay); 398 | } else { 399 | mfp._close(); 400 | } 401 | }, 402 | 403 | /** 404 | * Helper for close() function 405 | */ 406 | _close: function() { 407 | _mfpTrigger(CLOSE_EVENT); 408 | 409 | var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' '; 410 | 411 | mfp.bgOverlay.detach(); 412 | mfp.wrap.detach(); 413 | mfp.container.empty(); 414 | 415 | if(mfp.st.mainClass) { 416 | classesToRemove += mfp.st.mainClass + ' '; 417 | } 418 | 419 | mfp._removeClassFromMFP(classesToRemove); 420 | 421 | if(mfp.fixedContentPos) { 422 | var windowStyles = {marginRight: ''}; 423 | if(mfp.isIE7) { 424 | $('body, html').css('overflow', ''); 425 | } else { 426 | windowStyles.overflow = ''; 427 | } 428 | $('html').css(windowStyles); 429 | } 430 | 431 | _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS); 432 | mfp.ev.off(EVENT_NS); 433 | 434 | // clean up DOM elements that aren't removed 435 | mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style'); 436 | mfp.bgOverlay.attr('class', 'mfp-bg'); 437 | mfp.container.attr('class', 'mfp-container'); 438 | 439 | // remove close button from target element 440 | if(mfp.st.showCloseBtn && 441 | (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) { 442 | if(mfp.currTemplate.closeBtn) 443 | mfp.currTemplate.closeBtn.detach(); 444 | } 445 | 446 | 447 | if(mfp.st.autoFocusLast && mfp._lastFocusedEl) { 448 | $(mfp._lastFocusedEl).focus(); // put tab focus back 449 | } 450 | mfp.currItem = null; 451 | mfp.content = null; 452 | mfp.currTemplate = null; 453 | mfp.prevHeight = 0; 454 | 455 | _mfpTrigger(AFTER_CLOSE_EVENT); 456 | }, 457 | 458 | updateSize: function(winHeight) { 459 | 460 | if(mfp.isIOS) { 461 | // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2 462 | var zoomLevel = document.documentElement.clientWidth / window.innerWidth; 463 | var height = window.innerHeight * zoomLevel; 464 | mfp.wrap.css('height', height); 465 | mfp.wH = height; 466 | } else { 467 | mfp.wH = winHeight || _window.height(); 468 | } 469 | // Fixes #84: popup incorrectly positioned with position:relative on body 470 | if(!mfp.fixedContentPos) { 471 | mfp.wrap.css('height', mfp.wH); 472 | } 473 | 474 | _mfpTrigger('Resize'); 475 | 476 | }, 477 | 478 | /** 479 | * Set content of popup based on current index 480 | */ 481 | updateItemHTML: function() { 482 | var item = mfp.items[mfp.index]; 483 | 484 | // Detach and perform modifications 485 | mfp.contentContainer.detach(); 486 | 487 | if(mfp.content) 488 | mfp.content.detach(); 489 | 490 | if(!item.parsed) { 491 | item = mfp.parseEl( mfp.index ); 492 | } 493 | 494 | var type = item.type; 495 | 496 | _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]); 497 | // BeforeChange event works like so: 498 | // _mfpOn('BeforeChange', function(e, prevType, newType) { }); 499 | 500 | mfp.currItem = item; 501 | 502 | if(!mfp.currTemplate[type]) { 503 | var markup = mfp.st[type] ? mfp.st[type].markup : false; 504 | 505 | // allows to modify markup 506 | _mfpTrigger('FirstMarkupParse', markup); 507 | 508 | if(markup) { 509 | mfp.currTemplate[type] = $(markup); 510 | } else { 511 | // if there is no markup found we just define that template is parsed 512 | mfp.currTemplate[type] = true; 513 | } 514 | } 515 | 516 | if(_prevContentType && _prevContentType !== item.type) { 517 | mfp.container.removeClass('mfp-'+_prevContentType+'-holder'); 518 | } 519 | 520 | var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]); 521 | mfp.appendContent(newContent, type); 522 | 523 | item.preloaded = true; 524 | 525 | _mfpTrigger(CHANGE_EVENT, item); 526 | _prevContentType = item.type; 527 | 528 | // Append container back after its content changed 529 | mfp.container.prepend(mfp.contentContainer); 530 | 531 | _mfpTrigger('AfterChange'); 532 | }, 533 | 534 | 535 | /** 536 | * Set HTML content of popup 537 | */ 538 | appendContent: function(newContent, type) { 539 | mfp.content = newContent; 540 | 541 | if(newContent) { 542 | if(mfp.st.showCloseBtn && mfp.st.closeBtnInside && 543 | mfp.currTemplate[type] === true) { 544 | // if there is no markup, we just append close button element inside 545 | if(!mfp.content.find('.mfp-close').length) { 546 | mfp.content.append(_getCloseBtn()); 547 | } 548 | } else { 549 | mfp.content = newContent; 550 | } 551 | } else { 552 | mfp.content = ''; 553 | } 554 | 555 | _mfpTrigger(BEFORE_APPEND_EVENT); 556 | mfp.container.addClass('mfp-'+type+'-holder'); 557 | 558 | mfp.contentContainer.append(mfp.content); 559 | }, 560 | 561 | 562 | /** 563 | * Creates Magnific Popup data object based on given data 564 | * @param {int} index Index of item to parse 565 | */ 566 | parseEl: function(index) { 567 | var item = mfp.items[index], 568 | type; 569 | 570 | if(item.tagName) { 571 | item = { el: $(item) }; 572 | } else { 573 | type = item.type; 574 | item = { data: item, src: item.src }; 575 | } 576 | 577 | if(item.el) { 578 | var types = mfp.types; 579 | 580 | // check for 'mfp-TYPE' class 581 | for(var i = 0; i < types.length; i++) { 582 | if( item.el.hasClass('mfp-'+types[i]) ) { 583 | type = types[i]; 584 | break; 585 | } 586 | } 587 | 588 | item.src = item.el.attr('data-mfp-src'); 589 | if(!item.src) { 590 | item.src = item.el.attr('href'); 591 | } 592 | } 593 | 594 | item.type = type || mfp.st.type || 'inline'; 595 | item.index = index; 596 | item.parsed = true; 597 | mfp.items[index] = item; 598 | _mfpTrigger('ElementParse', item); 599 | 600 | return mfp.items[index]; 601 | }, 602 | 603 | 604 | /** 605 | * Initializes single popup or a group of popups 606 | */ 607 | addGroup: function(el, options) { 608 | var eHandler = function(e) { 609 | e.mfpEl = this; 610 | mfp._openClick(e, el, options); 611 | }; 612 | 613 | if(!options) { 614 | options = {}; 615 | } 616 | 617 | var eName = 'click.magnificPopup'; 618 | options.mainEl = el; 619 | 620 | if(options.items) { 621 | options.isObj = true; 622 | el.off(eName).on(eName, eHandler); 623 | } else { 624 | options.isObj = false; 625 | if(options.delegate) { 626 | el.off(eName).on(eName, options.delegate , eHandler); 627 | } else { 628 | options.items = el; 629 | el.off(eName).on(eName, eHandler); 630 | } 631 | } 632 | }, 633 | _openClick: function(e, el, options) { 634 | var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick; 635 | 636 | 637 | if(!midClick && ( e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey ) ) { 638 | return; 639 | } 640 | 641 | var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn; 642 | 643 | if(disableOn) { 644 | if($.isFunction(disableOn)) { 645 | if( !disableOn.call(mfp) ) { 646 | return true; 647 | } 648 | } else { // else it's number 649 | if( _window.width() < disableOn ) { 650 | return true; 651 | } 652 | } 653 | } 654 | 655 | if(e.type) { 656 | e.preventDefault(); 657 | 658 | // This will prevent popup from closing if element is inside and popup is already opened 659 | if(mfp.isOpen) { 660 | e.stopPropagation(); 661 | } 662 | } 663 | 664 | options.el = $(e.mfpEl); 665 | if(options.delegate) { 666 | options.items = el.find(options.delegate); 667 | } 668 | mfp.open(options); 669 | }, 670 | 671 | 672 | /** 673 | * Updates text on preloader 674 | */ 675 | updateStatus: function(status, text) { 676 | 677 | if(mfp.preloader) { 678 | if(_prevStatus !== status) { 679 | mfp.container.removeClass('mfp-s-'+_prevStatus); 680 | } 681 | 682 | if(!text && status === 'loading') { 683 | text = mfp.st.tLoading; 684 | } 685 | 686 | var data = { 687 | status: status, 688 | text: text 689 | }; 690 | // allows to modify status 691 | _mfpTrigger('UpdateStatus', data); 692 | 693 | status = data.status; 694 | text = data.text; 695 | 696 | mfp.preloader.html(text); 697 | 698 | mfp.preloader.find('a').on('click', function(e) { 699 | e.stopImmediatePropagation(); 700 | }); 701 | 702 | mfp.container.addClass('mfp-s-'+status); 703 | _prevStatus = status; 704 | } 705 | }, 706 | 707 | 708 | /* 709 | "Private" helpers that aren't private at all 710 | */ 711 | // Check to close popup or not 712 | // "target" is an element that was clicked 713 | _checkIfClose: function(target) { 714 | 715 | if($(target).hasClass(PREVENT_CLOSE_CLASS)) { 716 | return; 717 | } 718 | 719 | var closeOnContent = mfp.st.closeOnContentClick; 720 | var closeOnBg = mfp.st.closeOnBgClick; 721 | 722 | if(closeOnContent && closeOnBg) { 723 | return true; 724 | } else { 725 | 726 | // We close the popup if click is on close button or on preloader. Or if there is no content. 727 | if(!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0]) ) { 728 | return true; 729 | } 730 | 731 | // if click is outside the content 732 | if( (target !== mfp.content[0] && !$.contains(mfp.content[0], target)) ) { 733 | if(closeOnBg) { 734 | // last check, if the clicked element is in DOM, (in case it's removed onclick) 735 | if( $.contains(document, target) ) { 736 | return true; 737 | } 738 | } 739 | } else if(closeOnContent) { 740 | return true; 741 | } 742 | 743 | } 744 | return false; 745 | }, 746 | _addClassToMFP: function(cName) { 747 | mfp.bgOverlay.addClass(cName); 748 | mfp.wrap.addClass(cName); 749 | }, 750 | _removeClassFromMFP: function(cName) { 751 | this.bgOverlay.removeClass(cName); 752 | mfp.wrap.removeClass(cName); 753 | }, 754 | _hasScrollBar: function(winHeight) { 755 | return ( (mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()) ); 756 | }, 757 | _setFocus: function() { 758 | (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus(); 759 | }, 760 | _onFocusIn: function(e) { 761 | if( e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target) ) { 762 | mfp._setFocus(); 763 | return false; 764 | } 765 | }, 766 | _parseMarkup: function(template, values, item) { 767 | var arr; 768 | if(item.data) { 769 | values = $.extend(item.data, values); 770 | } 771 | _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item] ); 772 | 773 | $.each(values, function(key, value) { 774 | if(value === undefined || value === false) { 775 | return true; 776 | } 777 | arr = key.split('_'); 778 | if(arr.length > 1) { 779 | var el = template.find(EVENT_NS + '-'+arr[0]); 780 | 781 | if(el.length > 0) { 782 | var attr = arr[1]; 783 | if(attr === 'replaceWith') { 784 | if(el[0] !== value[0]) { 785 | el.replaceWith(value); 786 | } 787 | } else if(attr === 'img') { 788 | if(el.is('img')) { 789 | el.attr('src', value); 790 | } else { 791 | el.replaceWith( $('').attr('src', value).attr('class', el.attr('class')) ); 792 | } 793 | } else { 794 | el.attr(arr[1], value); 795 | } 796 | } 797 | 798 | } else { 799 | template.find(EVENT_NS + '-'+key).html(value); 800 | } 801 | }); 802 | }, 803 | 804 | _getScrollbarSize: function() { 805 | // thx David 806 | if(mfp.scrollbarSize === undefined) { 807 | var scrollDiv = document.createElement("div"); 808 | scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;'; 809 | document.body.appendChild(scrollDiv); 810 | mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth; 811 | document.body.removeChild(scrollDiv); 812 | } 813 | return mfp.scrollbarSize; 814 | } 815 | 816 | }; /* MagnificPopup core prototype end */ 817 | 818 | 819 | 820 | 821 | /** 822 | * Public static functions 823 | */ 824 | $.magnificPopup = { 825 | instance: null, 826 | proto: MagnificPopup.prototype, 827 | modules: [], 828 | 829 | open: function(options, index) { 830 | _checkInstance(); 831 | 832 | if(!options) { 833 | options = {}; 834 | } else { 835 | options = $.extend(true, {}, options); 836 | } 837 | 838 | options.isObj = true; 839 | options.index = index || 0; 840 | return this.instance.open(options); 841 | }, 842 | 843 | close: function() { 844 | return $.magnificPopup.instance && $.magnificPopup.instance.close(); 845 | }, 846 | 847 | registerModule: function(name, module) { 848 | if(module.options) { 849 | $.magnificPopup.defaults[name] = module.options; 850 | } 851 | $.extend(this.proto, module.proto); 852 | this.modules.push(name); 853 | }, 854 | 855 | defaults: { 856 | 857 | // Info about options is in docs: 858 | // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options 859 | 860 | disableOn: 0, 861 | 862 | key: null, 863 | 864 | midClick: false, 865 | 866 | mainClass: '', 867 | 868 | preloader: true, 869 | 870 | focus: '', // CSS selector of input to focus after popup is opened 871 | 872 | closeOnContentClick: false, 873 | 874 | closeOnBgClick: true, 875 | 876 | closeBtnInside: true, 877 | 878 | showCloseBtn: true, 879 | 880 | enableEscapeKey: true, 881 | 882 | modal: false, 883 | 884 | alignTop: false, 885 | 886 | removalDelay: 0, 887 | 888 | prependTo: null, 889 | 890 | fixedContentPos: 'auto', 891 | 892 | fixedBgPos: 'auto', 893 | 894 | overflowY: 'auto', 895 | 896 | closeMarkup: '', 897 | 898 | tClose: 'Close (Esc)', 899 | 900 | tLoading: 'Loading...', 901 | 902 | autoFocusLast: true 903 | 904 | } 905 | }; 906 | 907 | 908 | 909 | $.fn.magnificPopup = function(options) { 910 | _checkInstance(); 911 | 912 | var jqEl = $(this); 913 | 914 | // We call some API method of first param is a string 915 | if (typeof options === "string" ) { 916 | 917 | if(options === 'open') { 918 | var items, 919 | itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup, 920 | index = parseInt(arguments[1], 10) || 0; 921 | 922 | if(itemOpts.items) { 923 | items = itemOpts.items[index]; 924 | } else { 925 | items = jqEl; 926 | if(itemOpts.delegate) { 927 | items = items.find(itemOpts.delegate); 928 | } 929 | items = items.eq( index ); 930 | } 931 | mfp._openClick({mfpEl:items}, jqEl, itemOpts); 932 | } else { 933 | if(mfp.isOpen) 934 | mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1)); 935 | } 936 | 937 | } else { 938 | // clone options obj 939 | options = $.extend(true, {}, options); 940 | 941 | /* 942 | * As Zepto doesn't support .data() method for objects 943 | * and it works only in normal browsers 944 | * we assign "options" object directly to the DOM element. FTW! 945 | */ 946 | if(_isJQ) { 947 | jqEl.data('magnificPopup', options); 948 | } else { 949 | jqEl[0].magnificPopup = options; 950 | } 951 | 952 | mfp.addGroup(jqEl, options); 953 | 954 | } 955 | return jqEl; 956 | }; 957 | 958 | /*>>core*/ 959 | 960 | /*>>inline*/ 961 | 962 | var INLINE_NS = 'inline', 963 | _hiddenClass, 964 | _inlinePlaceholder, 965 | _lastInlineElement, 966 | _putInlineElementsBack = function() { 967 | if(_lastInlineElement) { 968 | _inlinePlaceholder.after( _lastInlineElement.addClass(_hiddenClass) ).detach(); 969 | _lastInlineElement = null; 970 | } 971 | }; 972 | 973 | $.magnificPopup.registerModule(INLINE_NS, { 974 | options: { 975 | hiddenClass: 'hide', // will be appended with `mfp-` prefix 976 | markup: '', 977 | tNotFound: 'Content not found' 978 | }, 979 | proto: { 980 | 981 | initInline: function() { 982 | mfp.types.push(INLINE_NS); 983 | 984 | _mfpOn(CLOSE_EVENT+'.'+INLINE_NS, function() { 985 | _putInlineElementsBack(); 986 | }); 987 | }, 988 | 989 | getInline: function(item, template) { 990 | 991 | _putInlineElementsBack(); 992 | 993 | if(item.src) { 994 | var inlineSt = mfp.st.inline, 995 | el = $(item.src); 996 | 997 | if(el.length) { 998 | 999 | // If target element has parent - we replace it with placeholder and put it back after popup is closed 1000 | var parent = el[0].parentNode; 1001 | if(parent && parent.tagName) { 1002 | if(!_inlinePlaceholder) { 1003 | _hiddenClass = inlineSt.hiddenClass; 1004 | _inlinePlaceholder = _getEl(_hiddenClass); 1005 | _hiddenClass = 'mfp-'+_hiddenClass; 1006 | } 1007 | // replace target inline element with placeholder 1008 | _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass); 1009 | } 1010 | 1011 | mfp.updateStatus('ready'); 1012 | } else { 1013 | mfp.updateStatus('error', inlineSt.tNotFound); 1014 | el = $('
'); 1015 | } 1016 | 1017 | item.inlineElement = el; 1018 | return el; 1019 | } 1020 | 1021 | mfp.updateStatus('ready'); 1022 | mfp._parseMarkup(template, {}, item); 1023 | return template; 1024 | } 1025 | } 1026 | }); 1027 | 1028 | /*>>inline*/ 1029 | 1030 | /*>>ajax*/ 1031 | var AJAX_NS = 'ajax', 1032 | _ajaxCur, 1033 | _removeAjaxCursor = function() { 1034 | if(_ajaxCur) { 1035 | $(document.body).removeClass(_ajaxCur); 1036 | } 1037 | }, 1038 | _destroyAjaxRequest = function() { 1039 | _removeAjaxCursor(); 1040 | if(mfp.req) { 1041 | mfp.req.abort(); 1042 | } 1043 | }; 1044 | 1045 | $.magnificPopup.registerModule(AJAX_NS, { 1046 | 1047 | options: { 1048 | settings: null, 1049 | cursor: 'mfp-ajax-cur', 1050 | tError: 'The content could not be loaded.' 1051 | }, 1052 | 1053 | proto: { 1054 | initAjax: function() { 1055 | mfp.types.push(AJAX_NS); 1056 | _ajaxCur = mfp.st.ajax.cursor; 1057 | 1058 | _mfpOn(CLOSE_EVENT+'.'+AJAX_NS, _destroyAjaxRequest); 1059 | _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest); 1060 | }, 1061 | getAjax: function(item) { 1062 | 1063 | if(_ajaxCur) { 1064 | $(document.body).addClass(_ajaxCur); 1065 | } 1066 | 1067 | mfp.updateStatus('loading'); 1068 | 1069 | var opts = $.extend({ 1070 | url: item.src, 1071 | success: function(data, textStatus, jqXHR) { 1072 | var temp = { 1073 | data:data, 1074 | xhr:jqXHR 1075 | }; 1076 | 1077 | _mfpTrigger('ParseAjax', temp); 1078 | 1079 | mfp.appendContent( $(temp.data), AJAX_NS ); 1080 | 1081 | item.finished = true; 1082 | 1083 | _removeAjaxCursor(); 1084 | 1085 | mfp._setFocus(); 1086 | 1087 | setTimeout(function() { 1088 | mfp.wrap.addClass(READY_CLASS); 1089 | }, 16); 1090 | 1091 | mfp.updateStatus('ready'); 1092 | 1093 | _mfpTrigger('AjaxContentAdded'); 1094 | }, 1095 | error: function() { 1096 | _removeAjaxCursor(); 1097 | item.finished = item.loadError = true; 1098 | mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src)); 1099 | } 1100 | }, mfp.st.ajax.settings); 1101 | 1102 | mfp.req = $.ajax(opts); 1103 | 1104 | return ''; 1105 | } 1106 | } 1107 | }); 1108 | 1109 | /*>>ajax*/ 1110 | 1111 | /*>>image*/ 1112 | var _imgInterval, 1113 | _getTitle = function(item) { 1114 | if(item.data && item.data.title !== undefined) 1115 | return item.data.title; 1116 | 1117 | var src = mfp.st.image.titleSrc; 1118 | 1119 | if(src) { 1120 | if($.isFunction(src)) { 1121 | return src.call(mfp, item); 1122 | } else if(item.el) { 1123 | return item.el.attr(src) || ''; 1124 | } 1125 | } 1126 | return ''; 1127 | }; 1128 | 1129 | $.magnificPopup.registerModule('image', { 1130 | 1131 | options: { 1132 | markup: '
'+ 1133 | '
'+ 1134 | '
'+ 1135 | '
'+ 1136 | '
'+ 1137 | '
'+ 1138 | '
'+ 1139 | '
'+ 1140 | '
'+ 1141 | '
'+ 1142 | '
'+ 1143 | '
', 1144 | cursor: 'mfp-zoom-out-cur', 1145 | titleSrc: 'title', 1146 | verticalFit: true, 1147 | tError: 'The image could not be loaded.' 1148 | }, 1149 | 1150 | proto: { 1151 | initImage: function() { 1152 | var imgSt = mfp.st.image, 1153 | ns = '.image'; 1154 | 1155 | mfp.types.push('image'); 1156 | 1157 | _mfpOn(OPEN_EVENT+ns, function() { 1158 | if(mfp.currItem.type === 'image' && imgSt.cursor) { 1159 | $(document.body).addClass(imgSt.cursor); 1160 | } 1161 | }); 1162 | 1163 | _mfpOn(CLOSE_EVENT+ns, function() { 1164 | if(imgSt.cursor) { 1165 | $(document.body).removeClass(imgSt.cursor); 1166 | } 1167 | _window.off('resize' + EVENT_NS); 1168 | }); 1169 | 1170 | _mfpOn('Resize'+ns, mfp.resizeImage); 1171 | if(mfp.isLowIE) { 1172 | _mfpOn('AfterChange', mfp.resizeImage); 1173 | } 1174 | }, 1175 | resizeImage: function() { 1176 | var item = mfp.currItem; 1177 | if(!item || !item.img) return; 1178 | 1179 | if(mfp.st.image.verticalFit) { 1180 | var decr = 0; 1181 | // fix box-sizing in ie7/8 1182 | if(mfp.isLowIE) { 1183 | decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'),10); 1184 | } 1185 | item.img.css('max-height', mfp.wH-decr); 1186 | } 1187 | }, 1188 | _onImageHasSize: function(item) { 1189 | if(item.img) { 1190 | 1191 | item.hasSize = true; 1192 | 1193 | if(_imgInterval) { 1194 | clearInterval(_imgInterval); 1195 | } 1196 | 1197 | item.isCheckingImgSize = false; 1198 | 1199 | _mfpTrigger('ImageHasSize', item); 1200 | 1201 | if(item.imgHidden) { 1202 | if(mfp.content) 1203 | mfp.content.removeClass('mfp-loading'); 1204 | 1205 | item.imgHidden = false; 1206 | } 1207 | 1208 | } 1209 | }, 1210 | 1211 | /** 1212 | * Function that loops until the image has size to display elements that rely on it asap 1213 | */ 1214 | findImageSize: function(item) { 1215 | 1216 | var counter = 0, 1217 | img = item.img[0], 1218 | mfpSetInterval = function(delay) { 1219 | 1220 | if(_imgInterval) { 1221 | clearInterval(_imgInterval); 1222 | } 1223 | // decelerating interval that checks for size of an image 1224 | _imgInterval = setInterval(function() { 1225 | if(img.naturalWidth > 0) { 1226 | mfp._onImageHasSize(item); 1227 | return; 1228 | } 1229 | 1230 | if(counter > 200) { 1231 | clearInterval(_imgInterval); 1232 | } 1233 | 1234 | counter++; 1235 | if(counter === 3) { 1236 | mfpSetInterval(10); 1237 | } else if(counter === 40) { 1238 | mfpSetInterval(50); 1239 | } else if(counter === 100) { 1240 | mfpSetInterval(500); 1241 | } 1242 | }, delay); 1243 | }; 1244 | 1245 | mfpSetInterval(1); 1246 | }, 1247 | 1248 | getImage: function(item, template) { 1249 | 1250 | var guard = 0, 1251 | 1252 | // image load complete handler 1253 | onLoadComplete = function() { 1254 | if(item) { 1255 | if (item.img[0].complete) { 1256 | item.img.off('.mfploader'); 1257 | 1258 | if(item === mfp.currItem){ 1259 | mfp._onImageHasSize(item); 1260 | 1261 | mfp.updateStatus('ready'); 1262 | } 1263 | 1264 | item.hasSize = true; 1265 | item.loaded = true; 1266 | 1267 | _mfpTrigger('ImageLoadComplete'); 1268 | 1269 | } 1270 | else { 1271 | // if image complete check fails 200 times (20 sec), we assume that there was an error. 1272 | guard++; 1273 | if(guard < 200) { 1274 | setTimeout(onLoadComplete,100); 1275 | } else { 1276 | onLoadError(); 1277 | } 1278 | } 1279 | } 1280 | }, 1281 | 1282 | // image error handler 1283 | onLoadError = function() { 1284 | if(item) { 1285 | item.img.off('.mfploader'); 1286 | if(item === mfp.currItem){ 1287 | mfp._onImageHasSize(item); 1288 | mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) ); 1289 | } 1290 | 1291 | item.hasSize = true; 1292 | item.loaded = true; 1293 | item.loadError = true; 1294 | } 1295 | }, 1296 | imgSt = mfp.st.image; 1297 | 1298 | 1299 | var el = template.find('.mfp-img'); 1300 | if(el.length) { 1301 | var img = document.createElement('img'); 1302 | img.className = 'mfp-img'; 1303 | if(item.el && item.el.find('img').length) { 1304 | img.alt = item.el.find('img').attr('alt'); 1305 | } 1306 | item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError); 1307 | img.src = item.src; 1308 | 1309 | // without clone() "error" event is not firing when IMG is replaced by new IMG 1310 | // TODO: find a way to avoid such cloning 1311 | if(el.is('img')) { 1312 | item.img = item.img.clone(); 1313 | } 1314 | 1315 | img = item.img[0]; 1316 | if(img.naturalWidth > 0) { 1317 | item.hasSize = true; 1318 | } else if(!img.width) { 1319 | item.hasSize = false; 1320 | } 1321 | } 1322 | 1323 | mfp._parseMarkup(template, { 1324 | title: _getTitle(item), 1325 | img_replaceWith: item.img 1326 | }, item); 1327 | 1328 | mfp.resizeImage(); 1329 | 1330 | if(item.hasSize) { 1331 | if(_imgInterval) clearInterval(_imgInterval); 1332 | 1333 | if(item.loadError) { 1334 | template.addClass('mfp-loading'); 1335 | mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) ); 1336 | } else { 1337 | template.removeClass('mfp-loading'); 1338 | mfp.updateStatus('ready'); 1339 | } 1340 | return template; 1341 | } 1342 | 1343 | mfp.updateStatus('loading'); 1344 | item.loading = true; 1345 | 1346 | if(!item.hasSize) { 1347 | item.imgHidden = true; 1348 | template.addClass('mfp-loading'); 1349 | mfp.findImageSize(item); 1350 | } 1351 | 1352 | return template; 1353 | } 1354 | } 1355 | }); 1356 | 1357 | /*>>image*/ 1358 | 1359 | /*>>zoom*/ 1360 | var hasMozTransform, 1361 | getHasMozTransform = function() { 1362 | if(hasMozTransform === undefined) { 1363 | hasMozTransform = document.createElement('p').style.MozTransform !== undefined; 1364 | } 1365 | return hasMozTransform; 1366 | }; 1367 | 1368 | $.magnificPopup.registerModule('zoom', { 1369 | 1370 | options: { 1371 | enabled: false, 1372 | easing: 'ease-in-out', 1373 | duration: 300, 1374 | opener: function(element) { 1375 | return element.is('img') ? element : element.find('img'); 1376 | } 1377 | }, 1378 | 1379 | proto: { 1380 | 1381 | initZoom: function() { 1382 | var zoomSt = mfp.st.zoom, 1383 | ns = '.zoom', 1384 | image; 1385 | 1386 | if(!zoomSt.enabled || !mfp.supportsTransition) { 1387 | return; 1388 | } 1389 | 1390 | var duration = zoomSt.duration, 1391 | getElToAnimate = function(image) { 1392 | var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'), 1393 | transition = 'all '+(zoomSt.duration/1000)+'s ' + zoomSt.easing, 1394 | cssObj = { 1395 | position: 'fixed', 1396 | zIndex: 9999, 1397 | left: 0, 1398 | top: 0, 1399 | '-webkit-backface-visibility': 'hidden' 1400 | }, 1401 | t = 'transition'; 1402 | 1403 | cssObj['-webkit-'+t] = cssObj['-moz-'+t] = cssObj['-o-'+t] = cssObj[t] = transition; 1404 | 1405 | newImg.css(cssObj); 1406 | return newImg; 1407 | }, 1408 | showMainContent = function() { 1409 | mfp.content.css('visibility', 'visible'); 1410 | }, 1411 | openTimeout, 1412 | animatedImg; 1413 | 1414 | _mfpOn('BuildControls'+ns, function() { 1415 | if(mfp._allowZoom()) { 1416 | 1417 | clearTimeout(openTimeout); 1418 | mfp.content.css('visibility', 'hidden'); 1419 | 1420 | // Basically, all code below does is clones existing image, puts in on top of the current one and animated it 1421 | 1422 | image = mfp._getItemToZoom(); 1423 | 1424 | if(!image) { 1425 | showMainContent(); 1426 | return; 1427 | } 1428 | 1429 | animatedImg = getElToAnimate(image); 1430 | 1431 | animatedImg.css( mfp._getOffset() ); 1432 | 1433 | mfp.wrap.append(animatedImg); 1434 | 1435 | openTimeout = setTimeout(function() { 1436 | animatedImg.css( mfp._getOffset( true ) ); 1437 | openTimeout = setTimeout(function() { 1438 | 1439 | showMainContent(); 1440 | 1441 | setTimeout(function() { 1442 | animatedImg.remove(); 1443 | image = animatedImg = null; 1444 | _mfpTrigger('ZoomAnimationEnded'); 1445 | }, 16); // avoid blink when switching images 1446 | 1447 | }, duration); // this timeout equals animation duration 1448 | 1449 | }, 16); // by adding this timeout we avoid short glitch at the beginning of animation 1450 | 1451 | 1452 | // Lots of timeouts... 1453 | } 1454 | }); 1455 | _mfpOn(BEFORE_CLOSE_EVENT+ns, function() { 1456 | if(mfp._allowZoom()) { 1457 | 1458 | clearTimeout(openTimeout); 1459 | 1460 | mfp.st.removalDelay = duration; 1461 | 1462 | if(!image) { 1463 | image = mfp._getItemToZoom(); 1464 | if(!image) { 1465 | return; 1466 | } 1467 | animatedImg = getElToAnimate(image); 1468 | } 1469 | 1470 | animatedImg.css( mfp._getOffset(true) ); 1471 | mfp.wrap.append(animatedImg); 1472 | mfp.content.css('visibility', 'hidden'); 1473 | 1474 | setTimeout(function() { 1475 | animatedImg.css( mfp._getOffset() ); 1476 | }, 16); 1477 | } 1478 | 1479 | }); 1480 | 1481 | _mfpOn(CLOSE_EVENT+ns, function() { 1482 | if(mfp._allowZoom()) { 1483 | showMainContent(); 1484 | if(animatedImg) { 1485 | animatedImg.remove(); 1486 | } 1487 | image = null; 1488 | } 1489 | }); 1490 | }, 1491 | 1492 | _allowZoom: function() { 1493 | return mfp.currItem.type === 'image'; 1494 | }, 1495 | 1496 | _getItemToZoom: function() { 1497 | if(mfp.currItem.hasSize) { 1498 | return mfp.currItem.img; 1499 | } else { 1500 | return false; 1501 | } 1502 | }, 1503 | 1504 | // Get element postion relative to viewport 1505 | _getOffset: function(isLarge) { 1506 | var el; 1507 | if(isLarge) { 1508 | el = mfp.currItem.img; 1509 | } else { 1510 | el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem); 1511 | } 1512 | 1513 | var offset = el.offset(); 1514 | var paddingTop = parseInt(el.css('padding-top'),10); 1515 | var paddingBottom = parseInt(el.css('padding-bottom'),10); 1516 | offset.top -= ( $(window).scrollTop() - paddingTop ); 1517 | 1518 | 1519 | /* 1520 | 1521 | Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa. 1522 | 1523 | */ 1524 | var obj = { 1525 | width: el.width(), 1526 | // fix Zepto height+padding issue 1527 | height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop 1528 | }; 1529 | 1530 | // I hate to do this, but there is no another option 1531 | if( getHasMozTransform() ) { 1532 | obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)'; 1533 | } else { 1534 | obj.left = offset.left; 1535 | obj.top = offset.top; 1536 | } 1537 | return obj; 1538 | } 1539 | 1540 | } 1541 | }); 1542 | 1543 | 1544 | 1545 | /*>>zoom*/ 1546 | 1547 | /*>>iframe*/ 1548 | 1549 | var IFRAME_NS = 'iframe', 1550 | _emptyPage = '//about:blank', 1551 | 1552 | _fixIframeBugs = function(isShowing) { 1553 | if(mfp.currTemplate[IFRAME_NS]) { 1554 | var el = mfp.currTemplate[IFRAME_NS].find('iframe'); 1555 | if(el.length) { 1556 | // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug 1557 | if(!isShowing) { 1558 | el[0].src = _emptyPage; 1559 | } 1560 | 1561 | // IE8 black screen bug fix 1562 | if(mfp.isIE8) { 1563 | el.css('display', isShowing ? 'block' : 'none'); 1564 | } 1565 | } 1566 | } 1567 | }; 1568 | 1569 | $.magnificPopup.registerModule(IFRAME_NS, { 1570 | 1571 | options: { 1572 | markup: '
'+ 1573 | '
'+ 1574 | ''+ 1575 | '
', 1576 | 1577 | srcAction: 'iframe_src', 1578 | 1579 | // we don't care and support only one default type of URL by default 1580 | patterns: { 1581 | youtube: { 1582 | index: 'youtube.com', 1583 | id: 'v=', 1584 | src: '//www.youtube.com/embed/%id%?autoplay=1' 1585 | }, 1586 | vimeo: { 1587 | index: 'vimeo.com/', 1588 | id: '/', 1589 | src: '//player.vimeo.com/video/%id%?autoplay=1' 1590 | }, 1591 | gmaps: { 1592 | index: '//maps.google.', 1593 | src: '%id%&output=embed' 1594 | } 1595 | } 1596 | }, 1597 | 1598 | proto: { 1599 | initIframe: function() { 1600 | mfp.types.push(IFRAME_NS); 1601 | 1602 | _mfpOn('BeforeChange', function(e, prevType, newType) { 1603 | if(prevType !== newType) { 1604 | if(prevType === IFRAME_NS) { 1605 | _fixIframeBugs(); // iframe if removed 1606 | } else if(newType === IFRAME_NS) { 1607 | _fixIframeBugs(true); // iframe is showing 1608 | } 1609 | }// else { 1610 | // iframe source is switched, don't do anything 1611 | //} 1612 | }); 1613 | 1614 | _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function() { 1615 | _fixIframeBugs(); 1616 | }); 1617 | }, 1618 | 1619 | getIframe: function(item, template) { 1620 | var embedSrc = item.src; 1621 | var iframeSt = mfp.st.iframe; 1622 | 1623 | $.each(iframeSt.patterns, function() { 1624 | if(embedSrc.indexOf( this.index ) > -1) { 1625 | if(this.id) { 1626 | if(typeof this.id === 'string') { 1627 | embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id)+this.id.length, embedSrc.length); 1628 | } else { 1629 | embedSrc = this.id.call( this, embedSrc ); 1630 | } 1631 | } 1632 | embedSrc = this.src.replace('%id%', embedSrc ); 1633 | return false; // break; 1634 | } 1635 | }); 1636 | 1637 | var dataObj = {}; 1638 | if(iframeSt.srcAction) { 1639 | dataObj[iframeSt.srcAction] = embedSrc; 1640 | } 1641 | mfp._parseMarkup(template, dataObj, item); 1642 | 1643 | mfp.updateStatus('ready'); 1644 | 1645 | return template; 1646 | } 1647 | } 1648 | }); 1649 | 1650 | 1651 | 1652 | /*>>iframe*/ 1653 | 1654 | /*>>gallery*/ 1655 | /** 1656 | * Get looped index depending on number of slides 1657 | */ 1658 | var _getLoopedId = function(index) { 1659 | var numSlides = mfp.items.length; 1660 | if(index > numSlides - 1) { 1661 | return index - numSlides; 1662 | } else if(index < 0) { 1663 | return numSlides + index; 1664 | } 1665 | return index; 1666 | }, 1667 | _replaceCurrTotal = function(text, curr, total) { 1668 | return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total); 1669 | }; 1670 | 1671 | $.magnificPopup.registerModule('gallery', { 1672 | 1673 | options: { 1674 | enabled: false, 1675 | arrowMarkup: '', 1676 | preload: [0,2], 1677 | navigateByImgClick: true, 1678 | arrows: true, 1679 | 1680 | tPrev: 'Previous (Left arrow key)', 1681 | tNext: 'Next (Right arrow key)', 1682 | tCounter: '%curr% of %total%' 1683 | }, 1684 | 1685 | proto: { 1686 | initGallery: function() { 1687 | 1688 | var gSt = mfp.st.gallery, 1689 | ns = '.mfp-gallery'; 1690 | 1691 | mfp.direction = true; // true - next, false - prev 1692 | 1693 | if(!gSt || !gSt.enabled ) return false; 1694 | 1695 | _wrapClasses += ' mfp-gallery'; 1696 | 1697 | _mfpOn(OPEN_EVENT+ns, function() { 1698 | 1699 | if(gSt.navigateByImgClick) { 1700 | mfp.wrap.on('click'+ns, '.mfp-img', function() { 1701 | if(mfp.items.length > 1) { 1702 | mfp.next(); 1703 | return false; 1704 | } 1705 | }); 1706 | } 1707 | 1708 | _document.on('keydown'+ns, function(e) { 1709 | if (e.keyCode === 37) { 1710 | mfp.prev(); 1711 | } else if (e.keyCode === 39) { 1712 | mfp.next(); 1713 | } 1714 | }); 1715 | }); 1716 | 1717 | _mfpOn('UpdateStatus'+ns, function(e, data) { 1718 | if(data.text) { 1719 | data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length); 1720 | } 1721 | }); 1722 | 1723 | _mfpOn(MARKUP_PARSE_EVENT+ns, function(e, element, values, item) { 1724 | var l = mfp.items.length; 1725 | values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : ''; 1726 | }); 1727 | 1728 | _mfpOn('BuildControls' + ns, function() { 1729 | if(mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) { 1730 | var markup = gSt.arrowMarkup, 1731 | arrowLeft = mfp.arrowLeft = $( markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left') ).addClass(PREVENT_CLOSE_CLASS), 1732 | arrowRight = mfp.arrowRight = $( markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right') ).addClass(PREVENT_CLOSE_CLASS); 1733 | 1734 | arrowLeft.click(function() { 1735 | mfp.prev(); 1736 | }); 1737 | arrowRight.click(function() { 1738 | mfp.next(); 1739 | }); 1740 | 1741 | mfp.container.append(arrowLeft.add(arrowRight)); 1742 | } 1743 | }); 1744 | 1745 | _mfpOn(CHANGE_EVENT+ns, function() { 1746 | if(mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout); 1747 | 1748 | mfp._preloadTimeout = setTimeout(function() { 1749 | mfp.preloadNearbyImages(); 1750 | mfp._preloadTimeout = null; 1751 | }, 16); 1752 | }); 1753 | 1754 | 1755 | _mfpOn(CLOSE_EVENT+ns, function() { 1756 | _document.off(ns); 1757 | mfp.wrap.off('click'+ns); 1758 | mfp.arrowRight = mfp.arrowLeft = null; 1759 | }); 1760 | 1761 | }, 1762 | next: function() { 1763 | mfp.direction = true; 1764 | mfp.index = _getLoopedId(mfp.index + 1); 1765 | mfp.updateItemHTML(); 1766 | }, 1767 | prev: function() { 1768 | mfp.direction = false; 1769 | mfp.index = _getLoopedId(mfp.index - 1); 1770 | mfp.updateItemHTML(); 1771 | }, 1772 | goTo: function(newIndex) { 1773 | mfp.direction = (newIndex >= mfp.index); 1774 | mfp.index = newIndex; 1775 | mfp.updateItemHTML(); 1776 | }, 1777 | preloadNearbyImages: function() { 1778 | var p = mfp.st.gallery.preload, 1779 | preloadBefore = Math.min(p[0], mfp.items.length), 1780 | preloadAfter = Math.min(p[1], mfp.items.length), 1781 | i; 1782 | 1783 | for(i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) { 1784 | mfp._preloadItem(mfp.index+i); 1785 | } 1786 | for(i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) { 1787 | mfp._preloadItem(mfp.index-i); 1788 | } 1789 | }, 1790 | _preloadItem: function(index) { 1791 | index = _getLoopedId(index); 1792 | 1793 | if(mfp.items[index].preloaded) { 1794 | return; 1795 | } 1796 | 1797 | var item = mfp.items[index]; 1798 | if(!item.parsed) { 1799 | item = mfp.parseEl( index ); 1800 | } 1801 | 1802 | _mfpTrigger('LazyLoad', item); 1803 | 1804 | if(item.type === 'image') { 1805 | item.img = $('').on('load.mfploader', function() { 1806 | item.hasSize = true; 1807 | }).on('error.mfploader', function() { 1808 | item.hasSize = true; 1809 | item.loadError = true; 1810 | _mfpTrigger('LazyLoadError', item); 1811 | }).attr('src', item.src); 1812 | } 1813 | 1814 | 1815 | item.preloaded = true; 1816 | } 1817 | } 1818 | }); 1819 | 1820 | /*>>gallery*/ 1821 | 1822 | /*>>retina*/ 1823 | 1824 | var RETINA_NS = 'retina'; 1825 | 1826 | $.magnificPopup.registerModule(RETINA_NS, { 1827 | options: { 1828 | replaceSrc: function(item) { 1829 | return item.src.replace(/\.\w+$/, function(m) { return '@2x' + m; }); 1830 | }, 1831 | ratio: 1 // Function or number. Set to 1 to disable. 1832 | }, 1833 | proto: { 1834 | initRetina: function() { 1835 | if(window.devicePixelRatio > 1) { 1836 | 1837 | var st = mfp.st.retina, 1838 | ratio = st.ratio; 1839 | 1840 | ratio = !isNaN(ratio) ? ratio : ratio(); 1841 | 1842 | if(ratio > 1) { 1843 | _mfpOn('ImageHasSize' + '.' + RETINA_NS, function(e, item) { 1844 | item.img.css({ 1845 | 'max-width': item.img[0].naturalWidth / ratio, 1846 | 'width': '100%' 1847 | }); 1848 | }); 1849 | _mfpOn('ElementParse' + '.' + RETINA_NS, function(e, item) { 1850 | item.src = st.replaceSrc(item, ratio); 1851 | }); 1852 | } 1853 | } 1854 | 1855 | } 1856 | } 1857 | }); 1858 | 1859 | /*>>retina*/ 1860 | _checkInstance(); })); --------------------------------------------------------------------------------