vendor: 4,295 bytes, lines 1-115
1/* 2 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 * 4 * Uses the built in easing capabilities added In jQuery 1.1 5 * to offer multiple easing options 6 * 7 * TERMS OF USE - jQuery Easing 8 * 9 * Open source under the BSD License. 10 * 11 * Copyright Щ 2008 George McGinley Smith 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without modification, 15 * are permitted provided that the following conditions are met: 16 * 17 * Redistributions of source code must retain the above copyright notice, this list of 18 * conditions and the following disclaimer. 19 * Redistributions in binary form must reproduce the above copyright notice, this list 20 * of conditions and the following disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * Neither the name of the author nor the names of contributors may be used to endorse 24 * or promote products derived from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 28 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 * OF THE POSSIBILITY OF SUCH DAMAGE. 35 * 36*/ 37 38// t: current time, b: begInnIng value, c: change In value, d: duration 39jQuery.easing['jswing'] = jQuery.easing['swing']; 40 41jQuery.extend( jQuery.easing, 42{ 43 def: 'easeOutQuad', 44 swing: function (x, t, b, c, d) { 45 //alert(jQuery.easing.default); 46 return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 47 }, 48 easeInQuad: function (x, t, b, c, d) { 49 return c*(t/=d)*t + b; 50 }, 51 easeOutQuad: function (x, t, b, c, d) { 52 return -c *(t/=d)*(t-2) + b; 53 }, 54 easeInOutQuad: function (x, t, b, c, d) { 55 if ((t/=d/2) < 1) return c/2*t*t + b; 56 return -c/2 * ((--t)*(t-2) - 1) + b; 57 }, 58 easeInCubic: function (x, t, b, c, d) { 59 return c*(t/=d)*t*t + b; 60 }, 61 easeOutCubic: function (x, t, b, c, d) { 62 return c*((t=t/d-1)*t*t + 1) + b; 63 }, 64 easeInOutCubic: function (x, t, b, c, d) { 65 if ((t/=d/2) < 1) return c/2*t*t*t + b; 66 return c/2*((t-=2)*t*t + 2) + b; 67 }, 68 easeInQuart: function (x, t, b, c, d) { 69 return c*(t/=d)*t*t*t + b; 70 }, 71 easeOutQuart: function (x, t, b, c, d) { 72 return -c * ((t=t/d-1)*t*t*t - 1) + b; 73 }, 74 easeInOutQuart: function (x, t, b, c, d) { 75 if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 76 return -c/2 * ((t-=2)*t*t*t - 2) + b; 77 }, 78 easeInQuint: function (x, t, b, c, d) { 79 return c*(t/=d)*t*t*t*t + b; 80 }, 81 easeOutQuint: function (x, t, b, c, d) { 82 return c*((t=t/d-1)*t*t*t*t + 1) + b; 83 }, 84 easeInOutQuint: function (x, t, b, c, d) { 85 if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 86 return c/2*((t-=2)*t*t*t*t + 2) + b; 87 }, 88 easeInSine: function (x, t, b, c, d) { 89 return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 90 }, 91 easeOutSine: function (x, t, b, c, d) { 92 return c * Math.sin(t/d * (Math.PI/2)) + b; 93 }, 94 easeInOutSine: function (x, t, b, c, d) { 95 return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 96 }, 97 easeInExpo: function (x, t, b, c, d) { 98 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 99 }, 100 easeOutExpo: function (x, t, b, c, d) { 101 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 102 }, 103 easeInOutExpo: function (x, t, b, c, d) { 104 if (t==0) return b; 105 if (t==d) return b+c; 106 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 107 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 108 }, 109 easeInCirc: function (x, t, b, c, d) { 110 return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 111 }, 112 easeOutCirc: function (x, t, b, c, d) { 113 return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 114 }, 115 easeInOutCirc: function (x, t, b, c, d) {
116 if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 117 return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 118 }, 119 easeInElastic: function (x, t, b, c, d) { 120 var s=1.70158;var p=0;var a=c; 121 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 122 if (a < Math.abs(c)) { a=c; var s=p/4; } 123 else var s = p/(2*Math.PI) * Math.asin (c/a); 124 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 125 }, 126 easeOutElastic: function (x, t, b, c, d) { 127 var s=1.70158;var p=0;var a=c; 128 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 129 if (a < Math.abs(c)) { a=c; var s=p/4; } 130 else var s = p/(2*Math.PI) * Math.asin (c/a); 131 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 132 }, 133 easeInOutElastic: function (x, t, b, c, d) { 134 var s=1.70158;var p=0;var a=c; 135 if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 136 if (a < Math.abs(c)) { a=c; var s=p/4; } 137 else var s = p/(2*Math.PI) * Math.asin (c/a); 138 if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 139 return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 140 }, 141 easeInBack: function (x, t, b, c, d, s) { 142 if (s == undefined) s = 1.70158; 143 return c*(t/=d)*t*((s+1)*t - s) + b; 144 }, 145 easeOutBack: function (x, t, b, c, d, s) { 146 if (s == undefined) s = 1.70158; 147 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 148 }, 149 easeInOutBack: function (x, t, b, c, d, s) { 150 if (s == undefined) s = 1.70158; 151 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 152 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 153 }, 154 easeInBounce: function (x, t, b, c, d) { 155 return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 156 }, 157 easeOutBounce: function (x, t, b, c, d) { 158 if ((t/=d) < (1/2.75)) { 159 return c*(7.5625*t*t) + b; 160 } else if (t < (2/2.75)) { 161 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 162 } else if (t < (2.5/2.75)) { 163 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 164 } else { 165 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 166 } 167 }, 168 easeInOutBounce: function (x, t, b, c, d) { 169 if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 170 return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 171 } 172}); 173 174/* 175 * 176 * TERMS OF USE - EASING EQUATIONS 177 * 178 * Open source under the BSD License. 179 * 180 * Copyright Щ 2001 Robert Penner 181 * All rights reserved. 182 * 183 * Redistribution and use in source and binary forms, with or without modification, 184 * are permitted provided that the following conditions are met: 185 * 186 * Redistributions of source code must retain the above copyright notice, this list of 187 * conditions and the following disclaimer. 188 * Redistributions in binary form must reproduce the above copyright notice, this list 189 * of conditions and the following disclaimer in the documentation and/or other materials 190 * provided with the distribution. 191 * 192 * Neither the name of the author nor the names of contributors may be used to endorse 193 * or promote products derived from this software without specific prior written permission. 194 * 195 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 196 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 197 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 198 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 199 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 200 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 201 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 202 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 203 * OF THE POSSIBILITY OF SUCH DAMAGE. 204 * 205 */ 206/*! 207 * 208 * Copyright (c) David Bushell | http://dbushell.com/ 209 * 210 */ 211(function(window, document, undefined) 212{ 213 214 // helper functions 215 216 var trim = function(str) 217 { 218 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,''); 219 }; 220 221 var hasClass = function(el, cn) 222 { 223 return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1; 224 }; 225 226 var addClass = function(el, cn) 227 { 228 if (!hasClass(el, cn)) { 229 el.className = (el.className === '') ? cn : el.className + ' ' + cn; 230 } 231 }; 232 233 var removeClass = function(el, cn) 234 { 235 el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' ')); 236 }; 237 238 var hasParent = function(el, id) 239 { 240 if (el) { 241 do { 242 if (el.id === id) { 243 return true; 244 }
245 if (el.nodeType === 9) { 246 break; 247 } 248 } 249 while((el = el.parentNode)); 250 } 251 return false; 252 }; 253 254 // normalize vendor prefixes 255 256 var doc = document.documentElement; 257 258 var transform_prop = window.Modernizr.prefixed('transform'), 259 transition_prop = window.Modernizr.prefixed('transition'), 260 transition_end = (function() { 261 var props = { 262 'WebkitTransition' : 'webkitTransitionEnd', 263 'MozTransition' : 'transitionend', 264 'OTransition' : 'oTransitionEnd otransitionend', 265 'msTransition' : 'MSTransitionEnd', 266 'transition' : 'transitionend' 267 }; 268 return props.hasOwnProperty(transition_prop) ? props[transition_prop] : false; 269 })(); 270 271 window.App = (function() 272 { 273 274 var _init = false, app = { }; 275 276 var inner = document.getElementById('inner-wrap'), 277 278 nav_open = false, 279 280 nav_class = 'js-nav'; 281 282 283 app.init = function() 284 { 285 if (_init) { 286 return; 287 } 288 _init = true; 289 290 var closeNavEnd = function(e) 291 { 292 if (e && e.target === inner) { 293 document.removeEventListener(transition_end, closeNavEnd, false); 294 } 295 nav_open = false; 296 }; 297 298 app.closeNav =function() 299 { 300 if (nav_open) { 301 // close navigation after transition or immediately 302 var duration = (transition_end && transition_prop) ? parseFloat(window.getComputedStyle(inner, '')[transition_prop + 'Duration']) : 0; 303 if (duration > 0) { 304 document.addEventListener(transition_end, closeNavEnd, false); 305 } else { 306 closeNavEnd(null); 307 } 308 } 309 removeClass(doc, nav_class); 310 }; 311 312 app.openNav = function() 313 { 314 if (nav_open) { 315 return; 316 } 317 addClass(doc, nav_class); 318 nav_open = true; 319 }; 320 321 app.toggleNav = function(e) 322 { 323 if (nav_open && hasClass(doc, nav_class)) { 324 app.closeNav(); 325 } else { 326 app.openNav(); 327 } 328 if (e) { 329 e.preventDefault(); 330 } 331 }; 332 333 // open nav with main "nav" button 334 document.getElementById('nav-open-btn').addEventListener('click', app.toggleNav, false); 335 336 // close nav with main "close" button 337 document.getElementById('nav-close-btn').addEventListener('click', app.toggleNav, false); 338 339 /* //we hide the document click, so we can use the dropdowns on the off-canvas menu 340 // close nav by touching the partial off-screen content 341 document.addEventListener('click', function(e) 342 { 343 if (nav_open && !hasParent(e.target, 'nav')) { 344 e.preventDefault(); 345 app.closeNav(); 346 } 347 }, 348 true); 349 */ 350 351 addClass(doc, 'js-ready'); 352 353 }; 354 355 return app; 356 357 })(); 358 359 if (window.addEventListener) { 360 window.addEventListener('DOMContentLoaded', window.App.init, false); 361 } 362 363})(window, window.document); 364 365/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */ 366 367window.matchMedia || (window.matchMedia = function() { 368 "use strict"; 369 370 // For browsers that support matchMedium api such as IE 9 and webkit 371 var styleMedia = (window.styleMedia || window.media); 372 373 // For those that don't support matchMedium 374 if (!styleMedia) { 375 var style = document.createElement('style'), 376 script = document.getElementsByTagName('script')[0], 377 info = null; 378 379 style.type = 'text/css'; 380 style.id = 'matchmediajs-test'; 381 382 script.parentNode.insertBefore(style, script); 383 384 // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers 385 info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; 386 387 styleMedia = { 388 matchMedium: function(media) { 389 var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; 390 391 // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers 392 if (style.styleSheet) { 393 style.styleSheet.cssText = text; 394 } else { 395 style.textContent = text; 396 } 397 398 // Test if media query is true or false 399 return info.width === '1px'; 400 } 401 }; 402 } 403 404 return function(media) { 405 return { 406 matches: styleMedia.matchMedium(media || 'all'), 407 media: media || 'all'
408 }; 409 }; 410}()); 411 412/*! matchMedia() polyfill addListener/removeListener extension. Author & copyright (c) 2012: Scott Jehl. Dual MIT/BSD license */ 413(function(){ 414 // Bail out for browsers that have addListener support 415 if (window.matchMedia && window.matchMedia('all').addListener) { 416 return false; 417 } 418 419 var localMatchMedia = window.matchMedia, 420 hasMediaQueries = localMatchMedia('only all').matches, 421 isListening = false, 422 timeoutID = 0, // setTimeout for debouncing 'handleChange' 423 queries = [], // Contains each 'mql' and associated 'listeners' if 'addListener' is used 424 handleChange = function(evt) { 425 // Debounce 426 clearTimeout(timeoutID); 427 428 timeoutID = setTimeout(function() { 429 for (var i = 0, il = queries.length; i < il; i++) { 430 var mql = queries[i].mql, 431 listeners = queries[i].listeners || [], 432 matches = localMatchMedia(mql.media).matches; 433 434 // Update mql.matches value and call listeners 435 // Fire listeners only if transitioning to or from matched state 436 if (matches !== mql.matches) { 437 mql.matches = matches; 438 439 for (var j = 0, jl = listeners.length; j < jl; j++) { 440 listeners[j].call(window, mql); 441 } 442 } 443 } 444 }, 30); 445 }; 446 447 window.matchMedia = function(media) { 448 var mql = localMatchMedia(media), 449 listeners = [], 450 index = 0; 451 452 mql.addListener = function(listener) { 453 // Changes would not occur to css media type so return now (Affects IE <= 8) 454 if (!hasMediaQueries) { 455 return; 456 } 457 458 // Set up 'resize' listener for browsers that support CSS3 media queries (Not for IE <= 8) 459 // There should only ever be 1 resize listener running for performance 460 if (!isListening) { 461 isListening = true; 462 window.addEventListener('resize', handleChange, true); 463 } 464 465 // Push object only if it has not been pushed already 466 if (index === 0) { 467 index = queries.push({ 468 mql : mql, 469 listeners : listeners 470 }); 471 } 472 473 listeners.push(listener); 474 }; 475 476 mql.removeListener = function(listener) { 477 for (var i = 0, il = listeners.length; i < il; i++){ 478 if (listeners[i] === listener){ 479 listeners.splice(i, 1); 480 } 481 } 482 }; 483 484 return mql; 485 }; 486}()); 487 488/*! 489 * enquire.js v2.1.2 - Awesome Media Queries in JavaScript 490 * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js 491 * License: MIT (http://www.opensource.org/licenses/mit-license.php) 492 */ 493 494!function(a,b,c){var d=window.matchMedia;"undefined"!=typeof module&&module.exports?module.exports=c(d):"function"==typeof define&&define.amd?define(function(){return b[a]=c(d)}):b[a]=c(d)}("enquire",this,function(a){"use strict";function b(a,b){var c,d=0,e=a.length;for(d;e>d&&(c=b(a[d],d),c!==!1);d++);}function c(a){return"[object Array]"===Object.prototype.toString.apply(a)}function d(a){return"function"==typeof a}function e(a){this.options=a,!a.deferSetup&&this.setup()}function f(b,c){this.query=b,this.isUnconditional=c,this.handlers=[],this.mql=a(b);var d=this;this.listener=function(a){d.mql=a,d.assess()},this.mql.addListener(this.listener)}
494function g(){if(!a)throw new Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!a("only all").matches}return e.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(a){return this.options===a||this.options.match===a}},f.prototype={addHandler:function(a){var b=new e(a);this.handlers.push(b),this.matches()&&b.on()},removeHandler:function(a){var c=this.handlers;b(c,function(b,d){return b.equals(a)?(b.destroy(),!c.splice(d,1)):void 0})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){b(this.handlers,function(a){a.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var a=this.matches()?"on":"off";b(this.handlers,function(b){b[a]()})}},g.prototype={register:function(a,e,g){var h=this.queries,i=g&&this.browserIsIncapable;return h[a]||(h[a]=new f(a,i)),d(e)&&(e={match:e}),c(e)||(e=[e]),b(e,function(b){d(b)&&(b={match:b}),h[a].addHandler(b)}),this},unregister:function(a,b){var c=this.queries[a];return c&&(b?c.removeHandler(b):(c.clear(),delete this.queries[a])),this}},new g}); 495/*! 496 * Bootstrap v3.3.2 (http://getbootstrap.com) 497 * Copyright 2011-2015 Twitter, Inc. 498 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 499 */ 500if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.2",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.2",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&
vendor: 4,320 bytes, line 500
500c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({}
vendor: 4,713 bytes, line 500
500,f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".
500navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j<i.length-1&&j++,~j||(j=0),i.eq(j).trigger("focus")}}}};var h=a.fn.dropdown;a.fn.dropdown=d,a.fn.dropdown.Constructor=g,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=h,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",f,g.prototype.toggle).on("keydown.bs.dropdown.data-api",f,g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="menu"]',g.prototype.keydown).on("keydown.bs.dropdown.data-api",'[role="listbox"]',g.prototype.keydown)}(jQuery),+function(a){"use strict";function b(b,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},c.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data("bs.modal",f=new c(this,g)),"string"==typeof b?f[b](d):g.show&&f.show(d)})}var c=function(b,c){this.options=c,this.$body=a(document.body),this.$element=a(b),this.$backdrop=this.isShown=null,this.scrollbarWidth=0,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=300,c.BACKDROP_TRANSITION_DURATION=150,c.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},c.prototype.toggle=function(a){return this.isShown?this.hide():this.show(a)},c.prototype.show=function(b){var d=this,e=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var e=a.support.transition&&d.$element.hasClass("fade");d.$element.parent().length||d.$element.appendTo(d.$body),d.$element.show().scrollTop(0),d.options.backdrop&&d.adjustBackdrop(),d.adjustDialog(),e&&d.$element[0].offsetWidth,d.$element.addClass("in").attr("aria-hidden",!1),d.enforceFocus();var f=a.Event("shown.bs.modal",{relatedTarget:b});e?d.$element.find(".modal-dialog").one("bsTransitionEnd",function(){d.$element.trigger("focus").trigger(f)}).emulateTransitionEnd(c.TRANSITION_DURATION):d.$element.trigger("focus").trigger(f)}))},c.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",a.proxy(this.hideModal,this)).emulateTransitionEnd(c.TRANSITION_DURATION):this.hideModal())},c.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.trigger("focus")},this))},c.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},c.prototype.resize=function(){this.isShown?a(window).on("resize.bs.modal",a.proxy(this.handleUpdate,this)):a(window).off("resize.bs.modal")},c.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.$body.removeClass("modal-open"),a.resetAdjustments(),a.resetScrollbar(),a.$element.trigger("hidden.bs.modal")})},c.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},c.prototype.backdrop=function(b){var d=this,e=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var f=a.support.transition&&e;if(this.$backdrop=a('<div class="modal-backdrop '+e+'" />').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},c.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m<p.top?"bottom":"right"==h&&k.right+l>p.width?"left":"left"==h&&k.left-l<p.left?"right":h,f.removeClass(n).addClass(h)}var q=this.getCalculatedOffset(h,k,l,m);this.applyPlacement(q,h);var r=function(){var a=e.hoverState;e.$element.trigger("shown.bs."+e.type),e.hoverState=null,"out"==a&&e.leave(e)};a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",r).emulateTransitionEnd(c.TRANSITION_DURATION):r()}},c.prototype.applyPlacement=function(b,c){var d=this.tip(),e=d[0].offsetWidth,f=d[0].offsetHeight,g=parseInt(d.css("margin-top"),10),h=
vendor: 9,758 bytes, lines 500-501
500parseInt(d.css("margin-left"),10);isNaN(g)&&(g=0),isNaN(h)&&(h=0),b.top=b.top+g,b.left=b.left+h,a.offset.setOffset(d[0],a.extend({using:function(a){d.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),d.addClass("in");var i=d[0].offsetWidth,j=d[0].offsetHeight;"top"==c&&j!=f&&(b.top=b.top+f-j);var k=this.getViewportAdjustedDelta(c,b,i,j);k.left?b.left+=k.left:b.top+=k.top;var l=/top|bottom/.test(c),m=l?2*k.left-e+i:2*k.top-f+j,n=l?"offsetWidth":"offsetHeight";d.offset(b),this.replaceArrow(m,d[0][n],l)},c.prototype.replaceArrow=function(a,b,c){this.arrow().css(c?"left":"top",50*(1-a/b)+"%").css(c?"top":"left","")},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},c.prototype.hide=function(b){function d(){"in"!=e.hoverState&&f.detach(),e.$element.removeAttr("aria-describedby").trigger("hidden.bs."+e.type),b&&b()}var e=this,f=this.tip(),g=a.Event("hide.bs."+this.type);return this.$element.trigger(g),g.isDefaultPrevented()?void 0:(f.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?f.one("bsTransitionEnd",d).emulateTransitionEnd(c.TRANSITION_DURATION):d(),this.hoverState=null,this)},c.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},c.prototype.hasContent=function(){return this.getTitle()},c.prototype.getPosition=function(b){b=b||this.$element;var c=b[0],d="BODY"==c.tagName,e=c.getBoundingClientRect();null==e.width&&(e=a.extend({},e,{width:e.right-e.left,height:e.bottom-e.top}));var f=d?{top:0,left:0}:b.offset(),g={scroll:d?document.documentElement.scrollTop||document.body.scrollTop:b.scrollTop()},h=d?{width:a(window).width(),height:a(window).height()}:null;return a.extend({},e,g,h,f)},c.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},c.prototype.getViewportAdjustedDelta=function(a,b,c,d){var e={top:0,left:0};if(!this.$viewport)return e;var f=this.options.viewport&&this.options.viewport.padding||0,g=this.getPosition(this.$viewport);if(/right|left/.test(a)){var h=b.top-f-g.scroll,i=b.top+f-g.scroll+d;h<g.top?e.top=g.top-h:i>g.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;j<g.left?e.left=g.left-j:k>g.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.2",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.2",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<e[0])return this.activeTarget=null,this.clear();for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e() 501}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;
501e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.2",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a("body").height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); 502(function ($) { 503 'use strict'; 504 505 // Case insensitive search 506 $.expr[':'].icontains = function (obj, index, meta) { 507 return icontains($(obj).text(), meta[3]); 508 }; 509 510 // Case and accent insensitive search 511 $.expr[':'].aicontains = function (obj, index, meta) { 512 return icontains($(obj).data('normalizedText') || $(obj).text(), meta[3]); 513 }; 514 515 /** 516 * Actual implementation of the case insensitive search. 517 * @access private 518 * @param {String} haystack 519 * @param {String} needle 520 * @returns {boolean} 521 */ 522 function icontains(haystack, needle) { 523 return haystack.toUpperCase().indexOf(needle.toUpperCase()) > -1; 524 } 525 526 /** 527 * Remove all diatrics from the given text. 528 * @access private 529 * @param {String} text 530 * @returns {String} 531 */ 532 function normalizeToBase(text) { 533 var rExps = [ 534 {re: /[\xC0-\xC6]/g, ch: "A"}, 535 {re: /[\xE0-\xE6]/g, ch: "a"}, 536 {re: /[\xC8-\xCB]/g, ch: "E"}, 537 {re: /[\xE8-\xEB]/g, ch: "e"}, 538 {re: /[\xCC-\xCF]/g, ch: "I"}, 539 {re: /[\xEC-\xEF]/g, ch: "i"}, 540 {re: /[\xD2-\xD6]/g, ch: "O"}, 541 {re: /[\xF2-\xF6]/g, ch: "o"}, 542 {re: /[\xD9-\xDC]/g, ch: "U"}, 543 {re: /[\xF9-\xFC]/g, ch: "u"}, 544 {re: /[\xC7-\xE7]/g, ch: "c"}, 545 {re: /[\xD1]/g, ch: "N"}, 546 {re: /[\xF1]/g, ch: "n"} 547 ]; 548 $.each(rExps, function () { 549 text = text.replace(this.re, this.ch); 550 }); 551 return text; 552 } 553 554 555 function htmlEscape(html) { 556 var escapeMap = { 557 '&': '&', 558 '<': '<', 559 '>': '>', 560 '"': '"', 561 "'": ''', 562 '`': '`' 563 }; 564 var source = '(?:' + Object.keys(escapeMap).join('|') + ')', 565 testRegexp = new RegExp(source), 566 replaceRegexp = new RegExp(source, 'g'), 567 string = html == null ? '' : '' + html; 568 return testRegexp.test(string) ? string.replace(replaceRegexp, function (match) { 569 return escapeMap[match]; 570 }) : string; 571 } 572 573 var Selectpicker = function (element, options, e) { 574 if (e) { 575 e.stopPropagation(); 576 e.preventDefault(); 577 } 578 579 this.$element = $(element); 580 this.$newElement = null; 581 this.$button = null; 582 this.$menu = null; 583 this.$lis = null; 584 this.options = options; 585 586 // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a 587 // data-attribute) 588 if (this.options.title === null) { 589 this.options.title = this.$element.attr('title'); 590 } 591 592 //Expose public methods 593 this.val = Selectpicker.prototype.val; 594 this.render = Selectpicker.prototype.render; 595 this.refresh = Selectpicker.prototype.refresh; 596 this.setStyle = Selectpicker.prototype.setStyle; 597 this.selectAll = Selectpicker.prototype.selectAll; 598 this.deselectAll = Selectpicker.prototype.deselectAll; 599 this.destroy = Selectpicker.prototype.remove; 600 this.remove = Selectpicker.prototype.remove; 601 this.show = Selectpicker.prototype.show; 602 this.hide = Selectpicker.prototype.hide; 603 604 this.init(); 605 }; 606 607 Selectpicker.VERSION = '1.6.3'; 608 609 // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. 610 Selectpicker.DEFAULTS = { 611 noneSelectedText: 'Nothing selected', 612 noneResultsText: 'No results match', 613 countSelectedText: function (numSelected, numTotal) { 614 return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; 615 }, 616 maxOptionsText: function (numAll, numGroup) { 617 var arr = []; 618 619 arr[0] = (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)'; 620 arr[1] = (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'; 621 622 return arr; 623 }, 624 selectAllText: 'Select All', 625 deselectAllText: 'Deselect All', 626 multipleSeparator: ', ', 627 style: 'btn-default', 628 size: 'auto', 629 title: null, 630 selectedTextFormat: 'values', 631 width: false, 632 container: false, 633 hideDisabled: false, 634 showSubtext: false, 635 showIcon: true, 636 showContent: true, 637 dropupAuto: true, 638 header: false, 639 liveSearch: false, 640 actionsBox: false, 641 iconBase: 'glyphicon', 642 tickIcon: 'glyphicon-ok', 643 maxOptions: false, 644 mobile: false, 645 selectOnTab: false, 646 dropdownAlignRight: false,
647 searchAccentInsensitive: false 648 }; 649 650 Selectpicker.prototype = { 651 652 constructor: Selectpicker, 653 654 init: function () { 655 var that = this, 656 id = this.$element.attr('id'); 657 658 this.$element.hide(); 659 this.multiple = this.$element.prop('multiple'); 660 this.autofocus = this.$element.prop('autofocus'); 661 this.$newElement = this.createView(); 662 this.$element.after(this.$newElement); 663 this.$menu = this.$newElement.find('> .dropdown-menu'); 664 this.$button = this.$newElement.find('> button'); 665 this.$searchbox = this.$newElement.find('input'); 666 667 if (this.options.dropdownAlignRight) 668 this.$menu.addClass('dropdown-menu-right'); 669 670 if (typeof id !== 'undefined') { 671 this.$button.attr('data-id', id); 672 $('label[for="' + id + '"]').click(function (e) { 673 e.preventDefault(); 674 that.$button.focus(); 675 }); 676 } 677 678 this.checkDisabled(); 679 this.clickListener(); 680 if (this.options.liveSearch) this.liveSearchListener(); 681 this.render(); 682 this.liHeight(); 683 this.setStyle(); 684 this.setWidth(); 685 if (this.options.container) this.selectPosition(); 686 this.$menu.data('this', this); 687 this.$newElement.data('this', this); 688 if (this.options.mobile) this.mobile(); 689 }, 690 691 createDropdown: function () { 692 // Options 693 // If we are multiple, then add the show-tick class by default 694 var multiple = this.multiple ? ' show-tick' : '', 695 inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', 696 autofocus = this.autofocus ? ' autofocus' : '', 697 btnSize = this.$element.parents().hasClass('form-group-lg') ? ' btn-lg' : (this.$element.parents().hasClass('form-group-sm') ? ' btn-sm' : ''); 698 // Elements 699 var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>' + this.options.header + '</div>' : ''; 700 var searchbox = this.options.liveSearch ? '<div class="bs-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>' : ''; 701 var actionsbox = this.options.actionsBox ? '<div class="bs-actionsbox">' + 702 '<div class="btn-group btn-block">' + 703 '<button class="actions-btn bs-select-all btn btn-sm btn-default">' + 704 this.options.selectAllText + 705 '</button>' + 706 '<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' + 707 this.options.deselectAllText + 708 '</button>' + 709 '</div>' + 710 '</div>' : ''; 711 var drop = 712 '<div class="btn-group bootstrap-select' + multiple + inputGroup + '">' + 713 '<button type="button" class="btn dropdown-toggle selectpicker' + btnSize + '" data-toggle="dropdown"' + autofocus + '>' + 714 '<span class="filter-option pull-left"></span> ' + 715 '<span class="caret"></span>' + 716 '</button>' + 717 '<div class="dropdown-menu open">' + 718 header + 719 searchbox + 720 actionsbox + 721 '<ul class="dropdown-menu inner selectpicker" role="menu">' + 722 '</ul>' + 723 '</div>' + 724 '</div>'; 725 726 return $(drop); 727 }, 728 729 createView: function () { 730 var $drop = this.createDropdown(); 731 var $li = this.createLi(); 732 $drop.find('ul').append($li); 733 return $drop; 734 }, 735 736 reloadLi: function () { 737 //Remove all children. 738 this.destroyLi(); 739 //Re build 740 var $li = this.createLi(); 741 this.$menu.find('ul').append($li); 742 }, 743 744 destroyLi: function () { 745 this.$menu.find('li').remove(); 746 }, 747 748 createLi: function () { 749 var that = this, 750 _li = [], 751 optID = 0; 752 753 // Helper functions 754 /** 755 * @param content 756 * @param [index] 757 * @param [classes] 758 * @returns {string} 759 */ 760 var generateLI = function (content, index, classes) { 761 return '<li' + 762 (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + 763 (typeof index !== 'undefined' | null === index ? ' data-original-index="' + index + '"' : '') + 764 '>' + content + '</li>'; 765 }; 766 767 /** 768 * @param text 769 * @param [classes] 770 * @param [inline] 771 * @param [optgroup] 772 * @returns {string} 773 */ 774 var generateA = function (text, classes, inline, optgroup) { 775 var normText = normalizeToBase(htmlEscape(text)); 776 return '<a tabindex="0"' + 777 (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + 778 (typeof inline !== 'undefined' ? ' style="' + inline + '"' : '') + 779 (typeof optgroup !== 'undefined' ? 'data-optgroup="' + optgroup + '"' : '') + 780 ' data-normalized-text="' + normText + '"' + 781 '>
781' + text + 782 '<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' + 783 '</a>'; 784 }; 785 786 this.$element.find('option').each(function () { 787 var $this = $(this); 788 789 // Get the class and text for the option 790 var optionClass = $this.attr('class') || '', 791 inline = $this.attr('style'), 792 text = $this.data('content') ? $this.data('content') : $this.html(), 793 subtext = typeof $this.data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.data('subtext') + '</small>' : '', 794 icon = typeof $this.data('icon') !== 'undefined' ? '<span class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></span> ' : '', 795 isDisabled = $this.is(':disabled') || $this.parent().is(':disabled'), 796 index = $this[0].index; 797 if (icon !== '' && isDisabled) { 798 icon = '<span>' + icon + '</span>'; 799 } 800 801 if (!$this.data('content')) { 802 // Prepend any icon and append any subtext to the main text. 803 text = icon + '<span class="text">' + text + subtext + '</span>'; 804 } 805 806 if (that.options.hideDisabled && isDisabled) { 807 return; 808 } 809 810 if ($this.parent().is('optgroup') && $this.data('divider') !== true) { 811 if ($this.index() === 0) { // Is it the first option of the optgroup? 812 optID += 1; 813 814 // Get the opt group label 815 var label = $this.parent().attr('label'); 816 var labelSubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.parent().data('subtext') + '</small>' : ''; 817 var labelIcon = $this.parent().data('icon') ? '<span class="' + that.options.iconBase + ' ' + $this.parent().data('icon') + '"></span> ' : ''; 818 label = labelIcon + '<span class="text">' + label + labelSubtext + '</span>'; 819 820 if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? 821 _li.push(generateLI('', null, 'divider')); 822 } 823 824 _li.push(generateLI(label, null, 'dropdown-header')); 825 } 826 827 _li.push(generateLI(generateA(text, 'opt ' + optionClass, inline, optID), index)); 828 } else if ($this.data('divider') === true) { 829 _li.push(generateLI('', index, 'divider')); 830 } else if ($this.data('hidden') === true) { 831 _li.push(generateLI(generateA(text, optionClass, inline), index, 'hide is-hidden')); 832 } else { 833 _li.push(generateLI(generateA(text, optionClass, inline), index)); 834 } 835 }); 836 837 //If we are not multiple, we don't have a selected item, and we don't have a title, select the first element so something is set in the button 838 if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) { 839 this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); 840 } 841 842 return $(_li.join('')); 843 }, 844 845 findLis: function () { 846 if (this.$lis == null) this.$lis = this.$menu.find('li'); 847 return this.$lis; 848 }, 849 850 /** 851 * @param [updateLi] defaults to true 852 */ 853 render: function (updateLi) { 854 var that = this; 855 856 //Update the LI to match the SELECT 857 if (updateLi !== false) { 858 this.$element.find('option').each(function (index) { 859 that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled')); 860 that.setSelected(index, $(this).is(':selected')); 861 }); 862 } 863 864 this.tabIndex(); 865 var notDisabled = this.options.hideDisabled ? ':not([disabled])' : ''; 866 var selectedItems = this.$element.find('option:selected' + notDisabled).map(function () { 867 var $this = $(this); 868 var icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : ''; 869 var subtext; 870 if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) { 871 subtext = ' <small class="muted text-muted">' + $this.data('subtext') + '</small>'; 872 } else { 873 subtext = ''; 874 } 875 if ($this.data('content') && that.options.showContent) { 876 return $this.data('content'); 877 } else if (typeof $this.attr('title') !== 'undefined') { 878 return $this.attr('title'); 879 } else { 880 return icon + $this.html() + subtext; 881 } 882 }).toArray(); 883 884 //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled 885 //Convert all the values into a comma delimited string 886 var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); 887 888 //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. 889 if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { 890 var max = this.options.selectedTextFormat.split('>'); 891 if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { 892 notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; 893 var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, 894 tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; 895 title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); 896 } 897 } 898 899 this.options.title = this.$element.attr('title'); 900 901 if (this.options.selectedTextFormat == 'static') { 902 title = this.options.title; 903 } 904 905 //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text 906 if (!title) { 907 title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; 908 } 909 910 this.$button.attr('title', htmlEscape(title)); 911 this.$newElement.find('.filter-option').html(title); 912 }, 913 914 /** 915 * @param [style] 916 * @param [status] 917 */ 918 setStyle: function (style, status) { 919 if (this.$element.attr('class')) { 920 this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|validate\[.*\]/gi, '')); 921 } 922 923 var buttonClass = style ? style : this.options.style; 924 925 if (status == 'add') { 926 this.$button.addClass(buttonClass); 927 } else if (status == 'remove') { 928 this.$button.removeClass(buttonClass); 929 } else { 930 this.$button.removeClass(this.options.style); 931 this.$button.addClass(buttonClass); 932 } 933 }, 934 935 liHeight: function () { 936 if (this.options.size === false) return; 937 938 var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'), 939 $menuClone = $selectClone.addClass('open').find('> .dropdown-menu'), 940 liHeight = $menuClone.find('li').not('.divider').not('.dropdown-header').filter(':visible').children('a').outerHeight(), 941 headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0, 942 searchHeight = this.options.liveSearch ? $menuClone.find('.bs-searchbox').outerHeight() : 0, 943 actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0; 944 945 $selectClone.remove(); 946 947 this.$newElement 948 .data('liHeight', liHeight) 949 .data('headerHeight', headerHeight) 950 .data('searchHeight', searchHeight) 951 .data('actionsHeight', actionsHeight); 952 }, 953 954 setSize: function () { 955 this.findLis(); 956 var that = this, 957 menu = this.$menu, 958 menuInner = menu.find('.inner'), 959 selectHeight = this.$newElement.outerHeight(), 960 liHeight = this.$newElement.data('liHeight'), 961 headerHeight = this.$newElement.data('headerHeight'), 962 searchHeight = this.$newElement.data('searchHeight'), 963 actionsHeight = this.$newElement.data('actionsHeight'), 964 divHeight = this.$lis.filter('.divider').outerHeight(true), 965 menuPadding = parseInt(menu.css('padding-top')) + 966 parseInt(menu.css('padding-bottom')) + 967 parseInt(menu.css('border-top-width')) + 968 parseInt(menu.css('border-bottom-width')), 969 notDisabled = this.options.hideDisabled ? ', .disabled' : '', 970 $window = $(window), 971 menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2, 972 menuHeight, 973 selectOffsetTop, 974 selectOffsetBot, 975 posVert = function () { 976 // JQuery defines a scrollTop function, but in pure JS it's a property 977 //noinspection JSValidateTypes 978 selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); 979 selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; 980 }; 981 posVert(); 982 if (this.options.header) menu.css('padding-top', 0); 983 984 if (this.options.size == 'auto') { 985 var getSize = function () { 986 var minHeight, 987 lisVis = that.$lis.not('.hide'); 988 989 posVert(); 990 menuHeight = selectOffsetBot - menuExtras; 991 992 if (that.options.dropupAuto) { 993 that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height())); 994 } 995 if (that.$newElement.hasClass('dropup')) { 996 menuHeight = selectOffsetTop - menuExtras; 997 } 998 999 if ((lisVis.length + lisVis.filter('.dropdown-header').length) > 3) { 1000 minHeight = liHeight * 3 + menuExtras - 2; 1001 } else { 1002 minHeight = 0; 1003 } 1004 1005 menu.css({ 1006 'max-height': menuHeight + 'px', 1007 'overflow': 'hidden', 1008 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px' 1009 }); 1010 menuInner.css({ 1011 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 1012 'overflow-y': 'auto', 1013 'min-height': Math.max(minHeight - menuPadding, 0) + 'px' 1014 }); 1015 }; 1016 getSize(); 1017 this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); 1018 $(window).off('resize.getSize').on('resize.getSize', getSize); 1019 $(window).off('scroll.getSize').on('scroll.getSize', getSize); 1020 } else if (this.options.size && this.options.size != 'auto' && menu.find('li' + notDisabled).length > this.options.size) { 1021 var optIndex = this.$lis.not('.divider' + notDisabled).find(' > *').slice(0, this.options.size).last().parent().index(); 1022 var divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; 1023 menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding; 1024 if (that.options.dropupAuto) { 1025 //noinspection JSUnusedAssignment 1026 this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height())); 1027 } 1028 menu.css({'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow': 'hidden'}); 1029 menuInner.css({'max-height': menuHeight - menuPadding + 'px', 'overflow-y': 'auto'}); 1030 } 1031 }, 1032 1033 setWidth: function () { 1034 if (this.options.width == 'auto') { 1035 this.$menu.css('min-width', '0'); 1036 1037 // Get correct width if element hidden 1038 var selectClone = this.$newElement.clone().appendTo('body'); 1039 var ulWidth = selectClone.find('> .dropdown-menu').css('width'); 1040 var btnWidth = selectClone.css('width', 'auto').find('> button').css('width'); 1041 selectClone.remove(); 1042 1043 // Set width to whatever's larger, button title or longest option 1044 this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px'); 1045 } else if (this.options.width == 'fit') { 1046 // Remove inline min-width so width can be changed from 'auto' 1047 this.$menu.css('min-width', ''); 1048 this.$newElement.css('width', '').addClass('fit-width'); 1049 } else if (this.options.width) { 1050 // Remove inline min-width so width can be changed from 'auto' 1051 this.$menu.css('min-width', ''); 1052 this.$newElement.css('width', this.options.width); 1053 } else { 1054 // Remove inline min-width/width so width can be changed 1055 this.$menu.css('min-width', ''); 1056 this.$newElement.css('width', ''); 1057 } 1058 // Remove fit-width class if width is changed programmatically 1059 if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { 1060 this.$newElement.removeClass('fit-width'); 1061 } 1062 }, 1063 1064 selectPosition: function () { 1065 var that = this, 1066 drop = '<div />', 1067 $drop = $(drop), 1068 pos, 1069 actualHeight, 1070 getPlacement = function ($element) { 1071 $drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); 1072 pos = $element.offset(); 1073 actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; 1074 $drop.css({ 1075 'top': pos.top + actualHeight, 1076 'left': pos.left, 1077 'width': $element[0].offsetWidth, 1078 'position': 'absolute' 1079 }); 1080 }; 1081 this.$newElement.on('click', function () { 1082 if (that.isDisabled()) { 1083 return; 1084 } 1085 getPlacement($(this)); 1086 $drop.appendTo(that.options.container); 1087 $drop.toggleClass('open', !$(this).hasClass('open')); 1088 $drop.append(that.$menu); 1089 }); 1090 $(window).resize(function () { 1091 getPlacement(that.$newElement); 1092 }); 1093 $(window).on('scroll', function () { 1094 getPlacement(that.$newElement); 1095 }); 1096 $('html').on('click', function (e) { 1097 if ($(e.target).closest(that.$newElement).length < 1) { 1098 $drop.removeClass('open'); 1099 } 1100 }); 1101 }, 1102 1103 setSelected: function (index, selected) { 1104 this.findLis(); 1105 this.$lis.filter('[data-original-index="' + index + '"]').toggleClass('selected', selected); 1106 }, 1107 1108 setDisabled: function (index, disabled) { 1109 this.findLis(); 1110 if (disabled) { 1111 this.$lis.filter('[data-original-index="' + index + '"]').addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1); 1112 } else { 1113 this.$lis.filter('[data-original-index="' + index + '"]').removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0); 1114 } 1115 }, 1116 1117 isDisabled: function () { 1118 return this.$element.is(':disabled'); 1119 }, 1120 1121 checkDisabled: function () { 1122 var that = this; 1123 1124 if (this.isDisabled()) { 1125 this.$button.addClass('disabled').attr('tabindex', -1); 1126 } else { 1127 if (this.$button.hasClass('disabled')) { 1128 this.$button.removeClass('disabled'); 1129 } 1130 1131 if (this.$button.attr('tabindex') == -1) { 1132 if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex'); 1133 } 1134 } 1135 1136 this.$button.click(function () { 1137 return !that.isDisabled(); 1138 }); 1139 }, 1140 1141 tabIndex: function () { 1142 if (this.$element.is('[tabindex]')) { 1143 this.$element.data('tabindex', this.$element.attr('tabindex')); 1144 this.$button.attr('tabindex', this.$element.data('tabindex')); 1145 } 1146 }, 1147 1148 clickListener: function () { 1149 var that = this; 1150 1151 this.$newElement.on('touchstart.dropdown', '.dropdown-menu', function (e) { 1152 e.stopPropagation(); 1153 }); 1154 1155 this.$newElement.on('click', function () { 1156
1156 that.setSize(); 1157 if (!that.options.liveSearch && !that.multiple) { 1158 setTimeout(function () { 1159 that.$menu.find('.selected a').focus(); 1160 }, 10); 1161 } 1162 }); 1163 1164 this.$menu.on('click', 'li a', function (e) { 1165 var $this = $(this), 1166 clickedIndex = $this.parent().data('originalIndex'), 1167 prevValue = that.$element.val(), 1168 prevIndex = that.$element.prop('selectedIndex'); 1169 1170 // Don't close on multi choice menu 1171 if (that.multiple) { 1172 e.stopPropagation(); 1173 } 1174 1175 e.preventDefault(); 1176 1177 //Don't run if we have been disabled 1178 if (!that.isDisabled() && !$this.parent().hasClass('disabled')) { 1179 var $options = that.$element.find('option'), 1180 $option = $options.eq(clickedIndex), 1181 state = $option.prop('selected'), 1182 $optgroup = $option.parent('optgroup'), 1183 maxOptions = that.options.maxOptions, 1184 maxOptionsGrp = $optgroup.data('maxOptions') || false; 1185 1186 if (!that.multiple) { // Deselect all others if not multi select box 1187 $options.prop('selected', false); 1188 $option.prop('selected', true); 1189 that.$menu.find('.selected').removeClass('selected'); 1190 that.setSelected(clickedIndex, true); 1191 } else { // Toggle the one we have chosen if we are multi select. 1192 $option.prop('selected', !state); 1193 that.setSelected(clickedIndex, !state); 1194 $this.blur(); 1195 1196 if ((maxOptions !== false) || (maxOptionsGrp !== false)) { 1197 var maxReached = maxOptions < $options.filter(':selected').length, 1198 maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; 1199 1200 if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { 1201 if (maxOptions && maxOptions == 1) { 1202 $options.prop('selected', false); 1203 $option.prop('selected', true); 1204 that.$menu.find('.selected').removeClass('selected'); 1205 that.setSelected(clickedIndex, true); 1206 } else if (maxOptionsGrp && maxOptionsGrp == 1) { 1207 $optgroup.find('option:selected').prop('selected', false); 1208 $option.prop('selected', true); 1209 var optgroupID = $this.data('optgroup'); 1210 1211 that.$menu.find('.selected').has('a[data-optgroup="' + optgroupID + '"]').removeClass('selected'); 1212 1213 that.setSelected(clickedIndex, true); 1214 } else { 1215 var maxOptionsArr = (typeof that.options.maxOptionsText === 'function') ? 1216 that.options.maxOptionsText(maxOptions, maxOptionsGrp) : that.options.maxOptionsText, 1217 maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), 1218 maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), 1219 $notify = $('<div class="notify"></div>'); 1220 // If {var} is set in array, replace it 1221 /** @deprecated */ 1222 if (maxOptionsArr[2]) { 1223 maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); 1224 maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); 1225 } 1226 1227 $option.prop('selected', false); 1228 1229 that.$menu.append($notify); 1230 1231 if (maxOptions && maxReached) { 1232 $notify.append($('<div>' + maxTxt + '</div>')); 1233 that.$element.trigger('maxReached.bs.select'); 1234 } 1235 1236 if (maxOptionsGrp && maxReachedGrp) { 1237 $notify.append($('<div>' + maxTxtGrp + '</div>')); 1238 that.$element.trigger('maxReachedGrp.bs.select'); 1239 } 1240 1241 setTimeout(function () { 1242 that.setSelected(clickedIndex, false); 1243 }, 10); 1244 1245 $notify.delay(750).fadeOut(300, function () { 1246 $(this).remove(); 1247 }); 1248 } 1249 } 1250 } 1251 } 1252 1253 if (!that.multiple) { 1254 that.$button.focus(); 1255 } else if (that.options.liveSearch) { 1256 that.$searchbox.focus(); 1257 } 1258 1259 // Trigger select 'change' 1260 if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { 1261 that.$element.change(); 1262 } 1263 } 1264 }); 1265 1266 this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) { 1267 if (e.target == this) { 1268 e.preventDefault(); 1269 e.stopPropagation(); 1270 if (!that.options.liveSearch) { 1271 that.$button.focus(); 1272 } else { 1273 that.$searchbox.focus(); 1274 } 1275 } 1276 }); 1277 1278 this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) { 1279 e.preventDefault(); 1280 e.stopPropagation(); 1281 if (!that.options.liveSearch) { 1282 that.$button.focus(); 1283 } else { 1284 that.$searchbox.focus(); 1285 } 1286 }); 1287 1288 this.$menu.on('click', '.popover-title .close', function () { 1289 that.$button.focus(); 1290 }); 1291 1292 this.$searchbox.on('click', function (e) { 1293 e.stopPropagation(); 1294 }); 1295 1296 1297 this.$menu.on('click', '.actions-btn', function (e) { 1298 if (that.options.liveSearch) { 1299 that.$searchbox.focus(); 1300 } else { 1301 that.$button.focus(); 1302 } 1303 1304 e.preventDefault(); 1305 e.stopPropagation(); 1306 1307 if ($(this).is('.bs-select-all')) { 1308 that.selectAll(); 1309 } else { 1310 that.deselectAll(); 1311 } 1312 that.$element.change(); 1313 }); 1314 1315 this.$element.change(function () { 1316 that.render(false); 1317 }); 1318 }, 1319 1320 liveSearchListener: function () { 1321 var that = this, 1322 no_results = $('<li class="no-results"></li>'); 1323 1324 this.$newElement.on('click.dropdown.data-api touchstart.dropdown.data-api', function () { 1325 that.$menu.find('.active').removeClass('active'); 1326 if (!!that.$searchbox.val()) { 1327 that.$searchbox.val(''); 1328 that.$lis.not('.is-hidden').removeClass('hide'); 1329 if (!!no_results.parent().length) no_results.remove(); 1330 } 1331 if (!that.multiple) that.$menu.find('.selected').addClass('active'); 1332 setTimeout(function () { 1333 that.$searchbox.focus(); 1334 }, 10); 1335 }); 1336 1337 this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function (e) { 1338 e.stopPropagation(); 1339 }); 1340 1341 this.$searchbox.on('input propertychange', function () { 1342 if (that.$searchbox.val()) { 1343
1344 if (that.options.searchAccentInsensitive) { 1345 that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':aicontains(' + normalizeToBase(that.$searchbox.val()) + ')').parent().addClass('hide'); 1346 } else { 1347 that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide'); 1348 } 1349 1350 if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) { 1351 if (!!no_results.parent().length) no_results.remove(); 1352 no_results.html(that.options.noneResultsText + ' "' + htmlEscape(that.$searchbox.val()) + '"').show(); 1353 that.$menu.find('li').last().after(no_results); 1354 } else if (!!no_results.parent().length) { 1355 no_results.remove(); 1356 } 1357 1358 } else { 1359 that.$lis.not('.is-hidden').removeClass('hide'); 1360 if (!!no_results.parent().length) no_results.remove(); 1361 } 1362 1363 that.$menu.find('li.active').removeClass('active'); 1364 that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus(); 1365 $(this).focus(); 1366 }); 1367 }, 1368 1369 val: function (value) { 1370 if (typeof value !== 'undefined') { 1371 this.$element.val(value); 1372 this.render(); 1373 1374 return this.$element; 1375 } else { 1376 return this.$element.val(); 1377 } 1378 }, 1379 1380 selectAll: function () { 1381 this.findLis(); 1382 this.$lis.not('.divider').not('.disabled').not('.selected').filter(':visible').find('a').click(); 1383 }, 1384 1385 deselectAll: function () { 1386 this.findLis(); 1387 this.$lis.not('.divider').not('.disabled').filter('.selected').filter(':visible').find('a').click(); 1388 }, 1389 1390 keydown: function (e) { 1391 var $this = $(this), 1392 $parent = ($this.is('input')) ? $this.parent().parent() : $this.parent(), 1393 $items, 1394 that = $parent.data('this'), 1395 index, 1396 next, 1397 first, 1398 last, 1399 prev, 1400 nextPrev, 1401 prevIndex, 1402 isActive, 1403 keyCodeMap = { 1404 32: ' ', 1405 48: '0', 1406 49: '1', 1407 50: '2', 1408 51: '3', 1409 52: '4', 1410 53: '5', 1411 54: '6', 1412 55: '7', 1413 56: '8', 1414 57: '9', 1415 59: ';', 1416 65: 'a', 1417 66: 'b', 1418 67: 'c', 1419 68: 'd', 1420 69: 'e', 1421 70: 'f', 1422 71: 'g', 1423 72: 'h', 1424 73: 'i', 1425 74: 'j', 1426 75: 'k', 1427 76: 'l', 1428 77: 'm', 1429 78: 'n', 1430 79: 'o', 1431 80: 'p', 1432 81: 'q', 1433 82: 'r', 1434 83: 's', 1435 84: 't', 1436 85: 'u', 1437 86: 'v', 1438 87: 'w', 1439 88: 'x', 1440 89: 'y', 1441 90: 'z', 1442 96: '0', 1443 97: '1', 1444 98: '2', 1445 99: '3', 1446 100: '4', 1447 101: '5', 1448 102: '6', 1449 103: '7', 1450 104: '8', 1451 105: '9' 1452 }; 1453 1454 if (that.options.liveSearch) $parent = $this.parent().parent(); 1455 1456 if (that.options.container) $parent = that.$menu; 1457 1458 $items = $('[role=menu] li a', $parent); 1459 1460 isActive = that.$menu.parent().hasClass('open'); 1461 1462 if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) { 1463 if (!that.options.container) { 1464 that.setSize(); 1465 that.$menu.parent().addClass('open'); 1466 isActive = true; 1467 } else { 1468 that.$newElement.trigger('click'); 1469 } 1470 that.$searchbox.focus(); 1471 } 1472 1473 if (that.options.liveSearch) { 1474 if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && that.$menu.find('.active').length === 0) { 1475 e.preventDefault(); 1476 that.$menu.parent().removeClass('open'); 1477 that.$button.focus(); 1478 } 1479 $items = $('[role=menu] li:not(.divider):not(.dropdown-header):visible', $parent); 1480 if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { 1481 if ($items.filter('.active').length === 0) {
1482 if (that.options.searchAccentInsensitive) { 1483 $items = that.$newElement.find('li').filter(':aicontains(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); 1484 } else { 1485 $items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')'); 1486 } 1487 } 1488 } 1489 } 1490 1491 if (!$items.length) return; 1492 1493 if (/(38|40)/.test(e.keyCode.toString(10))) { 1494 index = $items.index($items.filter(':focus')); 1495 first = $items.parent(':not(.disabled):visible').first().index(); 1496 last = $items.parent(':not(.disabled):visible').last().index(); 1497 next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index(); 1498 prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index(); 1499 nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index(); 1500 1501 if (that.options.liveSearch) { 1502 $items.each(function (i) { 1503 if ($(this).is(':not(.disabled)')) { 1504 $(this).data('index', i); 1505 } 1506 }); 1507 index = $items.index($items.filter('.active')); 1508 first = $items.filter(':not(.disabled):visible').first().data('index'); 1509 last = $items.filter(':not(.disabled):visible').last().data('index'); 1510 next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index'); 1511 prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index'); 1512 nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index'); 1513 } 1514 1515 prevIndex = $this.data('prevIndex'); 1516 1517 if (e.keyCode == 38) { 1518 if (that.options.liveSearch) index -= 1; 1519 if (index != nextPrev && index > prev) index = prev; 1520 if (index < first) index = first; 1521 if (index == prevIndex) index = last; 1522 } 1523 1524 if (e.keyCode == 40) { 1525 if (that.options.liveSearch) index += 1; 1526 if (index == -1) index = 0; 1527 if (index != nextPrev && index < next) index = next; 1528 if (index > last) index = last; 1529 if (index == prevIndex) index = first; 1530 } 1531 1532 $this.data('prevIndex', index); 1533 1534 if (!that.options.liveSearch) { 1535 $items.eq(index).focus(); 1536 } else { 1537 e.preventDefault(); 1538 if (!$this.is('.dropdown-toggle')) { 1539 $items.removeClass('active'); 1540 $items.eq(index).addClass('active').find('a').focus(); 1541 $this.focus(); 1542 } 1543 } 1544 1545 } else if (!$this.is('input')) { 1546 var keyIndex = [], 1547 count, 1548 prevKey; 1549 1550 $items.each(function () { 1551 if ($(this).parent().is(':not(.disabled)')) { 1552 if ($.trim($(this).text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { 1553 keyIndex.push($(this).parent().index()); 1554 } 1555 } 1556 }); 1557 1558 count = $(document).data('keycount'); 1559 count++; 1560 $(document).data('keycount', count); 1561 1562 prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1); 1563 1564 if (prevKey != keyCodeMap[e.keyCode]) { 1565 count = 1; 1566 $(document).data('keycount', count); 1567 } else if (count >= keyIndex.length) { 1568 $(document).data('keycount', 0); 1569 if (count > keyIndex.length) count = 1; 1570 } 1571 1572 $items.eq(keyIndex[count - 1]).focus(); 1573 } 1574 1575 // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. 1576 if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { 1577 if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); 1578 if (!that.options.liveSearch) { 1579 $(':focus').click(); 1580 } else if (!/(32)/.test(e.keyCode.toString(10))) { 1581 that.$menu.find('.active a').click(); 1582 $this.focus(); 1583 } 1584 $(document).data('keycount', 0); 1585 } 1586 1587 if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { 1588 that.$menu.parent().removeClass('open'); 1589 that.$button.focus(); 1590 } 1591 }, 1592 1593 mobile: function () { 1594 this.$element.addClass('mobile-device').appendTo(this.$newElement); 1595 if (this.options.container) this.$menu.hide(); 1596 }, 1597 1598 refresh: function () { 1599 this.$lis = null; 1600 this.reloadLi(); 1601 this.render(); 1602 this.setWidth(); 1603 this.setStyle(); 1604 this.checkDisabled(); 1605 this.liHeight(); 1606 }, 1607 1608 update: function () { 1609 this.reloadLi(); 1610 this.setWidth(); 1611 this.setStyle(); 1612 this.checkDisabled(); 1613 this.liHeight(); 1614 }, 1615 1616 hide: function () { 1617 this.$newElement.hide(); 1618 }, 1619 1620 show: function () { 1621 this.$newElement.show(); 1622 }, 1623 1624 remove: function () { 1625 this.$newElement.remove(); 1626 this.$element.remove(); 1627 } 1628 }; 1629 1630 // SELECTPICKER PLUGIN DEFINITION 1631 // ============================== 1632 function Plugin(option, event) { 1633 // get the args of the outer function.. 1634 var args = arguments; 1635 // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them 1636 // to get lost 1637 //noinspection JSDuplicatedDeclaration 1638 var _option = option, 1639 option = args[0], 1640 event = args[1]; 1641 [].shift.apply(args); 1642 1643 // This fixes a bug in the js implementation on android 2.3 #715 1644 if (typeof option == 'undefined') { 1645 option = _option; 1646 } 1647 1648 var value; 1649 var chain = this.each(function () { 1650 var $this = $(this); 1651 if ($this.is('select')) { 1652 var data = $this.data('selectpicker'), 1653 options = typeof option == 'object' && option; 1654 1655 if (!data) { 1656 var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); 1657 $this.data('selectpicker', (data = new Selectpicker(this, config, event))); 1658 } else if (options) { 1659 for (var i in options) { 1660 if (options.hasOwnProperty(i)) { 1661 data.options[i] = options[i]; 1662 } 1663 } 1664 } 1665 1666 if (typeof option == 'string') { 1667 if (data[option] instanceof Function) { 1668 value = data[option].apply(data, args); 1669 } else { 1670 value = data.options[option]; 1671 } 1672 } 1673 } 1674 });
1675 1676 if (typeof value !== 'undefined') { 1677 //noinspection JSUnusedAssignment 1678 return value; 1679 } else { 1680 return chain; 1681 } 1682 } 1683 1684 var old = $.fn.selectpicker; 1685 $.fn.selectpicker = Plugin; 1686 $.fn.selectpicker.Constructor = Selectpicker; 1687 1688 // SELECTPICKER NO CONFLICT 1689 // ======================== 1690 $.fn.selectpicker.noConflict = function () { 1691 $.fn.selectpicker = old; 1692 return this; 1693 }; 1694 1695 $(document) 1696 .data('keycount', 0) 1697 .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', Selectpicker.prototype.keydown) 1698 .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', function (e) { 1699 e.stopPropagation(); 1700 }); 1701 1702 // SELECTPICKER DATA-API 1703 // ===================== 1704 $(window).on('load.bs.select.data-api', function () { 1705 $('.selectpicker').each(function () { 1706 var $selectpicker = $(this); 1707 Plugin.call($selectpicker, $selectpicker.data()); 1708 }) 1709 }); 1710})(jQuery); 1711 1712/*! 1713 * VERSION: 1.16.0 1714 * DATE: 2015-03-01 1715 * UPDATES AND DOCS AT: http://greensock.com 1716 * 1717 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin 1718 * 1719 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 1720 * This work is subject to the terms at http://greensock.com/standard-license or for 1721 * Club GreenSock members, the software agreement that was issued with your membership. 1722 * 1723 * @author: Jack Doyle, [email protected] 1724 **/ 1725var _gsScope="undefined"!=typeof module&&module.exports&&"undefined"!=typeof global?global:this||window;(_gsScope._gsQueue||(_gsScope._gsQueue=[])).push(function(){"use strict";_gsScope._gsDefine("TweenMax",["core.Animation","core.SimpleTimeline","TweenLite"],function(t,e,i){var s=function(t){var e,i=[],s=t.length;for(e=0;e!==s;i.push(t[e++]));return i},r=function(t,e,s){i.call(this,t,e,s),this._cycle=0,this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._dirty=!0,this.render=r.prototype.render},n=1e-10,a=i._internals,o=a.isSelector,h=a.isArray,l=r.prototype=i.to({},.1,{}),_=[];r.version="1.16.0",l.constructor=r,l.kill()._gc=!1,r.killTweensOf=r.killDelayedCallsTo=i.killTweensOf,r.getTweensOf=i.getTweensOf,r.lagSmoothing=i.lagSmoothing,r.ticker=i.ticker,r.render=i.render,l.invalidate=function(){return this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._uncache(!0),i.prototype.invalidate.call(this)},l.updateTo=function(t,e){var s,r=this.ratio,n=this.vars.immediateRender||t.immediateRender;e&&this._startTime<this._timeline._time&&(this._startTime=this._timeline._time,this._uncache(!1),this._gc?this._enabled(!0,!1):this._timeline.insert(this,this._startTime-this._delay));for(s in t)this.vars[s]=t[s];if(this._initted||n)if(e)this._initted=!1,n&&this.render(0,!0,!0);else if(this._gc&&this._enabled(!0,!1),this._notifyPluginsOfEnabled&&this._firstPT&&i._onPluginEvent("_onDisable",this),this._time/this._duration>.998){var a=this._time;this.render(0,!0,!1),this._initted=!1,this.render(a,!0,!1)}else if(this._time>0||n){this._initted=!1,this._init();for(var o,h=1/(1-r),l=this._firstPT;l;)o=l.s+l.c,l.c*=h,l.s=o-l.c,l=l._next}return this},l.render=function(t,e,i){this._initted||0===this._duration&&this.vars.repeat&&this.invalidate();var s,r,o,h,l,u,p,f,c=this._dirty?this.totalDuration():this._totalDuration,m=this._time,d=this._totalTime,g=this._cycle,v=this._duration,y=this._rawPrevTime;if(t>=c?(this._totalTime=c,this._cycle=this._repeat,this._yoyo&&0!==(1&this._cycle)?(this._time=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0):(this._time=v,this.ratio=this._ease._calcEnd?this._ease.getRatio(1):1),this._reversed||(s=!0,r="onComplete"),0===v&&(this._initted||!this.vars.lazy||i)&&(this._startTime===this._timeline._duration&&(t=0),(0===t||0>y||y===n)&&y!==t&&(i=!0,y>n&&(r="onReverseComplete")),this._rawPrevTime=f=!e||t||y===t?t:n)):1e-7>t?(this._totalTime=this._time=this._cycle=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0,(0!==d||0===v&&y>0)&&(r="onReverseComplete",s=this._reversed),0>t&&(this._active=!1,0===v&&(this._initted||!this.vars.lazy||i)&&(y>=0&&(i=!0),this._rawPrevTime=f=!e||t||y===t?t:n)),this._initted||(i=!0)):(this._totalTime=this._time=t,0!==this._repeat&&(h=v+this._repeatDelay,this._cycle=this._totalTime/h>>0,0!==this._cycle&&this._cycle===this._totalTime/h&&this._cycle--,this._time=this._totalTime-this._cycle*h,this._yoyo&&0!==(1&this._cycle)&&(this._time=v-this._time),this._time>v?this._time=v:0>this._time&&(this._time=0)),this._easeType?(l=this._time/v,u=this._easeType,p=this._easePower,(1===u||3===u&&l>=.5)&&(l=1-l),3===u&&(l*=2),1===p?l*=l:2===p?l*=l*l:3===p?l*=l*l*l:4===p&&(l*=l*l*l*l),this.ratio=1===u?1-l:2===u?l:.5>this._time/v?l/2:1-l/2):this.ratio=this._ease.getRatio(this._time/v)),m===this._time&&!i&&g===this._cycle)return d!==this._totalTime&&this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUp
vendor: 9,856 bytes, line 1725
1725dateScope||this,this.vars.onUpdateParams||_)),void 0;if(!this._initted){if(this._init(),!this._initted||this._gc)return;if(!i&&this._firstPT&&(this.vars.lazy!==!1&&this._duration||this.vars.lazy&&!this._duration))return this._time=m,this._totalTime=d,this._rawPrevTime=y,this._cycle=g,a.lazyTweens.push(this),this._lazy=[t,e],void 0;this._time&&!s?this.ratio=this._ease.getRatio(this._time/v):s&&this._ease._calcEnd&&(this.ratio=this._ease.getRatio(0===this._time?0:1))}for(this._lazy!==!1&&(this._lazy=!1),this._active||!this._paused&&this._time!==m&&t>=0&&(this._active=!0),0===d&&(2===this._initted&&t>0&&this._init(),this._startAt&&(t>=0?this._startAt.render(t,e,i):r||(r="_dummyGS")),this.vars.onStart&&(0!==this._totalTime||0===v)&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||_))),o=this._firstPT;o;)o.f?o.t[o.p](o.c*this.ratio+o.s):o.t[o.p]=o.c*this.ratio+o.s,o=o._next;this._onUpdate&&(0>t&&this._startAt&&this._startTime&&this._startAt.render(t,e,i),e||(this._totalTime!==d||s)&&this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||_)),this._cycle!==g&&(e||this._gc||this.vars.onRepeat&&this.vars.onRepeat.apply(this.vars.onRepeatScope||this,this.vars.onRepeatParams||_)),r&&(!this._gc||i)&&(0>t&&this._startAt&&!this._onUpdate&&this._startTime&&this._startAt.render(t,e,i),s&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[r]&&this.vars[r].apply(this.vars[r+"Scope"]||this,this.vars[r+"Params"]||_),0===v&&this._rawPrevTime===n&&f!==n&&(this._rawPrevTime=0))},r.to=function(t,e,i){return new r(t,e,i)},r.from=function(t,e,i){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,new r(t,e,i)},r.fromTo=function(t,e,i,s){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,new r(t,e,s)},r.staggerTo=r.allTo=function(t,e,n,a,l,u,p){a=a||0;var f,c,m,d,g=n.delay||0,v=[],y=function(){n.onComplete&&n.onComplete.apply(n.onCompleteScope||this,arguments),l.apply(p||this,u||_)};for(h(t)||("string"==typeof t&&(t=i.selector(t)||t),o(t)&&(t=s(t))),t=t||[],0>a&&(t=s(t),t.reverse(),a*=-1),f=t.length-1,m=0;f>=m;m++){c={};for(d in n)c[d]=n[d];c.delay=g,m===f&&l&&(c.onComplete=y),v[m]=new r(t[m],e,c),g+=a}return v},r.staggerFrom=r.allFrom=function(t,e,i,s,n,a,o){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,r.staggerTo(t,e,i,s,n,a,o)},r.staggerFromTo=r.allFromTo=function(t,e,i,s,n,a,o,h){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,r.staggerTo(t,e,s,n,a,o,h)},r.delayedCall=function(t,e,i,s,n){return new r(e,0,{delay:t,onComplete:e,onCompleteParams:i,onCompleteScope:s,onReverseComplete:e,onReverseCompleteParams:i,onReverseCompleteScope:s,immediateRender:!1,useFrames:n,overwrite:0})},r.set=function(t,e){return new r(t,0,e)},r.isTweening=function(t){return i.getTweensOf(t,!0).length>0};var u=function(t,e){for(var s=[],r=0,n=t._first;n;)n instanceof i?s[r++]=n:(e&&(s[r++]=n),s=s.concat(u(n,e)),r=s.length),n=n._next;return s},p=r.getAllTweens=function(e){return u(t._rootTimeline,e).concat(u(t._rootFramesTimeline,e))};r.killAll=function(t,i,s,r){null==i&&(i=!0),null==s&&(s=!0);var n,a,o,h=p(0!=r),l=h.length,_=i&&s&&r;for(o=0;l>o;o++)a=h[o],(_||a instanceof e||(n=a.target===a.vars.onComplete)&&s||i&&!n)&&(t?a.totalTime(a._reversed?0:a.totalDuration()):a._enabled(!1,!1))},r.killChildTweensOf=function(t,e){if(null!=t){var n,l,_,u,p,f=a.tweenLookup;if("string"==typeof t&&(t=i.selector(t)||t),o(t)&&(t=s(t)),h(t))for(u=t.length;--u>-1;)r.killChildTweensOf(t[u],e);else{n=[];for(_ in f)for(l=f[_].target.parentNode;l;)l===t&&(n=n.concat(f[_].tweens)),l=l.parentNode;for(p=n.length,u=0;p>u;u++)e&&n[u].totalTime(n[u].totalDuration()),n[u]._enabled(!1,!1)}}};var f=function(t,i,s,r){i=i!==!1,s=s!==!1,r=r!==!1;for(var n,a,o=p(r),h=i&&s&&r,l=o.length;--l>-1;)a=o[l],(h||a instanceof e||(n=a.target===a.vars.onComplete)&&s||i&&!n)&&a.paused(t)};return r.pauseAll=function(t,e,i){f(!0,t,e,i)},r.resumeAll=function(t,e,i){f(!1,t,e,i)},r.globalTimeScale=function(e){var s=t._rootTimeline,r=i.ticker.time;return arguments.length?(e=e||n,s._startTime=r-(r-s._startTime)*s._timeScale/e,s=t._rootFramesTimeline,r=i.ticker.frame,s._startTime=r-(r-s._startTime)*s._timeScale/e,s._timeScale=t._rootTimeline._timeScale=e,e):s._timeScale},l.progress=function(t){return arguments.length?this.totalTime(this.duration()*(this._yoyo&&0!==(1&this._cycle)?1-t:t)+this._cycle*(this._duration+this._repeatDelay),!1):this._time/this.duration()},l.totalProgress=function(t){return arguments.length?this.totalTime(this.totalDuration()*t,!1):this._totalTime/this.totalDuration()},l.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),t>this._duration&&(t=this._duration),this._yoyo&&0!==(1&this._cycle)?t=this._duration-t+this._cycle*(this._duration+this._repeatDelay):0!==this._repeat&&(t+=this._cycle*(this._duration+this._repeatDelay)),this.totalTime(t,e)):this._time},l.duration=function(e){return arguments.length?t.prototype.duration.call(this,e):this._duration},l.totalDuration=function(t){return arguments.length?-1===this._repeat?this:this.duration((t-this._repeat*this._repeatDelay)/(this._repeat+1)):(this._dirty&&(this._totalDuration=-1===this._repeat?999999999999:this._duration*(this._repeat+1)+this._repeatDelay*this._repeat,this._dirty=!1),this._totalDuration)},l.repeat=function(t){return arguments.length?(this._repeat=t,this._uncache(!0)):this._repeat},l.repeatDelay=function(t){return arguments.length?(this._repeatDelay=t,this._uncache(!0)):this._repeatDelay},l.yoyo=function(t){return arguments.length?(this._yoyo=t,this):this._yoyo},r},!0),_gsScope._gsDefine("TimelineLite",["core.Animation","core.SimpleTimeline","TweenLite"],function(t,e,i){var s=function(t){e.call(this,t),this._labels={},this.autoRemoveChildren=this.vars.autoRemoveChildren===!0,this.smoothChildTiming=this.vars.smoothChildTiming===!0,this._sortChildren=!0,this._onUpdate=this.vars.onUpdate;var i,s,r=this.vars;for(s in r)i=r[s],h(i)&&-1!==i.join("").indexOf("{self}")&&(r[s]=this._swapSelfInParams(i));h(r.tweens)&&this.add(r.tweens,0,r.align,r.stagger)},r=1e-10,n=i._internals,a=s._internals={},o=n.isSelector,h=n.isArray,l=n.lazyTweens,_=n.lazyRender,u=[],p=_gsScope._gsDefine.globals,f=function(t){var e,i={};for(e in t)i[e]=t[e];return i},c=a.pauseCallback=function(t,e,i,s){var n,a=t._timeline,o=a._totalTime,h=t._startTime,l=t.ratio?r:0,_=t.ratio?0:r;if(e||!this._forcingPlayhead){for(a.pause(h),n=t._prev;n&&n._startTime===h;)n._rawPrevTime=_,n=n._prev;for(n=t._next;n&&n._startTime===h;)n._rawPrevTime=l,n=n._next;e&&e.apply(s||a,i||u),this._forcingPlayhead&&a.seek(o)}},m=function(t){var e,i=[],s=t.length;for(e=0;e!==s;i.push(t[e++]));return i},d=s.prototype=new e;return s.version="1.16.0",d.constructor=s,d.kill()._gc=d._forcingPlayhead=!1,d.to=function(t,e,s,r){var n=s.repeat&&p.TweenMax||i;return e?this.add(new n(t,e,s),r):this.set(t,s,r)},d.from=function(t,e,s,r){return this.add((s.repeat&&p.TweenMax||i).from(t,e,s),r)},d.fromTo=function(t,e,s,r,n){var a=r.repeat&&p.TweenMax||i;return e?this.add(a.fromTo(t,e,s,r),n):this.set(t,r,n)},d.staggerTo=function(t,e,r,n,a,h,l,_){var u,p=new s({onComplete:h,onCompleteParams:l,onCompleteScope:_,smoothChildTiming:this.smoothChildTiming});for("string"==typeof t&&(t=i.selector(t)||t),t=t||[],o(t)&&(t=m(t)),n=n||0,0>n&&(t=m(t),t.reverse(),n*=-1),u=0;t.length>u;u++)r.startAt&&(r.startAt=f(r.startAt)),p.to(t[u],e,f(r),u*n);return this.add(p,a)},d.staggerFrom=function(t,e,i,s,r,n,a,o){return i.immediateRender=0!=i.immediateRender,i.runBackwards=!0,this.staggerTo(t,e,i,s,r,n,a,o)},d.staggerFromTo=function(t,e,i,s,r,n,a,o,h){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,this.staggerTo(t,e,s,r,n,a,o,h)},d.call=function(t,e,s,r){return this.add(i.delayedCall(0,t,e,s),r)},d.set=function(t,e,s){return s=this._parseTimeOrLabel(s,0,!0),null==e.immediateRender&&(e.immediateRender=s===this._time&&!this._paused),this.add(new i(t,0,e),s)},s.exportRoot=function(t,e){t=t||{},null==t.smoothChildTiming&&(t.smoothChildTiming=!0);var r,n,a=new s(t),o=a._timeline;for(null==e&&(e=!0),o._remove(a,!0),a._startTime=0,a._rawPrevTime=a._time=a._totalTime=o._time,r=o._first;r;)n=r._next,e&&r instanceof i&&r.target===r.vars.onComplete||a.add(r,r._startTime-r._delay),r=n;return o.add(a,0),a},d.add=function(r,n,a,o){var l,_,u,p,f,c;if("number"!=typeof n&&(n=this._parseTimeOrLabel(n,0,!0,r)),!(r instanceof t)){if(r instanceof Array||r&&r.push&&h(r)){for(a=a||"normal",o=o||0,l=n,_=r.length,u=0;_>u;u++)h(p=r[u])&&(p=new s({tweens:p})),this.add(p,l),"string"!=typeof p&&"function"!=typeof p&&("sequence"===a?l=p._startTime+p.totalDuration()/p._timeScale:"start"===a&&(p._startTime-=p.delay())),l+=o;return this._uncache(!0)}if("string"==typeof r)return this.addLabel(r,n);if("function"!=typeof r)throw"Cannot add "+r+" into the timeline; it is not a tween, timeline, function, or string.";r=i.delayedCall(0,r)}if(e.prototype.add.call(this,r,n),(this._gc||this._time===this._duration)&&!this._paused&&this._duration<this.duration())for(f=this,c=f.rawTime()>r._startTime;f._timeline;)c&&f._timeline.smoothChildTiming?f.totalTime(f._totalTime,!0):f._gc&&f._enabled(!0,!1),f=f._timeline;return this},d.remove=function(e){if(e instanceof t)return this._remove(e,!1);if(e instanceof Array||e&&e.push&&h(e)){for(var i=e.length;--i>-1;)this.remove(e[i]);return this}return"string"==typeof e?this.removeLabel(e):this.kill(null,e)},d._remove=function(t,i){e.prototype._remove.call(this,t,i);var s=this._last;return s?this._time>s._startTime+s._totalDuration/s._timeScale&&(this._time=this.duration(),this._totalTime=this._totalDuration):this._time=this._totalTime=this._duration=this._totalDuration=0,this},d.append=function(t,e){return this.add(t,this._parseTimeOrLabel(null,e,!0,t))}
1725,d.insert=d.insertMultiple=function(t,e,i,s){return this.add(t,e||0,i,s)},d.appendMultiple=function(t,e,i,s){return this.add(t,this._parseTimeOrLabel(null,e,!0,t),i,s)},d.addLabel=function(t,e){return this._labels[t]=this._parseTimeOrLabel(e),this},d.addPause=function(t,e,s,r){var n=i.delayedCall(0,c,["{self}",e,s,r],this);return n.data="isPause",this.add(n,t)},d.removeLabel=function(t){return delete this._labels[t],this},d.getLabelTime=function(t){return null!=this._labels[t]?this._labels[t]:-1},d._parseTimeOrLabel=function(e,i,s,r){var n;if(r instanceof t&&r.timeline===this)this.remove(r);else if(r&&(r instanceof Array||r.push&&h(r)))for(n=r.length;--n>-1;)r[n]instanceof t&&r[n].timeline===this&&this.remove(r[n]);if("string"==typeof i)return this._parseTimeOrLabel(i,s&&"number"==typeof e&&null==this._labels[i]?e-this.duration():0,s);if(i=i||0,"string"!=typeof e||!isNaN(e)&&null==this._labels[e])null==e&&(e=this.duration());else{if(n=e.indexOf("="),-1===n)return null==this._labels[e]?s?this._labels[e]=this.duration()+i:i:this._labels[e]+i;i=parseInt(e.charAt(n-1)+"1",10)*Number(e.substr(n+1)),e=n>1?this._parseTimeOrLabel(e.substr(0,n-1),0,s):this.duration()}return Number(e)+i},d.seek=function(t,e){return this.totalTime("number"==typeof t?t:this._parseTimeOrLabel(t),e!==!1)},d.stop=function(){return this.paused(!0)},d.gotoAndPlay=function(t,e){return this.play(t,e)},d.gotoAndStop=function(t,e){return this.pause(t,e)},d.render=function(t,e,i){this._gc&&this._enabled(!0,!1);var s,n,a,o,h,p=this._dirty?this.totalDuration():this._totalDuration,f=this._time,c=this._startTime,m=this._timeScale,d=this._paused;if(t>=p)this._totalTime=this._time=p,this._reversed||this._hasPausedChild()||(n=!0,o="onComplete",0===this._duration&&(0===t||0>this._rawPrevTime||this._rawPrevTime===r)&&this._rawPrevTime!==t&&this._first&&(h=!0,this._rawPrevTime>r&&(o="onReverseComplete"))),this._rawPrevTime=this._duration||!e||t||this._rawPrevTime===t?t:r,t=p+1e-4;else if(1e-7>t)if(this._totalTime=this._time=0,(0!==f||0===this._duration&&this._rawPrevTime!==r&&(this._rawPrevTime>0||0>t&&this._rawPrevTime>=0))&&(o="onReverseComplete",n=this._reversed),0>t)this._active=!1,this._timeline.autoRemoveChildren&&this._reversed?(h=n=!0,o="onReverseComplete"):this._rawPrevTime>=0&&this._first&&(h=!0),this._rawPrevTime=t;else{if(this._rawPrevTime=this._duration||!e||t||this._rawPrevTime===t?t:r,0===t&&n)for(s=this._first;s&&0===s._startTime;)s._duration||(n=!1),s=s._next;t=0,this._initted||(h=!0)}else this._totalTime=this._time=this._rawPrevTime=t;if(this._time!==f&&this._first||i||h){if(this._initted||(this._initted=!0),this._active||!this._paused&&this._time!==f&&t>0&&(this._active=!0),0===f&&this.vars.onStart&&0!==this._time&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||u)),this._time>=f)for(s=this._first;s&&(a=s._next,!this._paused||d);)(s._active||s._startTime<=this._time&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=a;else for(s=this._last;s&&(a=s._prev,!this._paused||d);)(s._active||f>=s._startTime&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=a;this._onUpdate&&(e||(l.length&&_(),this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||u))),o&&(this._gc||(c===this._startTime||m!==this._timeScale)&&(0===this._time||p>=this.totalDuration())&&(n&&(l.length&&_(),this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[o]&&this.vars[o].apply(this.vars[o+"Scope"]||this,this.vars[o+"Params"]||u)))}},d._hasPausedChild=function(){for(var t=this._first;t;){if(t._paused||t instanceof s&&t._hasPausedChild())return!0;t=t._next}return!1},d.getChildren=function(t,e,s,r){r=r||-9999999999;for(var n=[],a=this._first,o=0;a;)r>a._startTime||(a instanceof i?e!==!1&&(n[o++]=a):(s!==!1&&(n[o++]=a),t!==!1&&(n=n.concat(a.getChildren(!0,e,s)),o=n.length))),a=a._next;return n},d.getTweensOf=function(t,e){var s,r,n=this._gc,a=[],o=0;for(n&&this._enabled(!0,!0),s=i.getTweensOf(t),r=s.length;--r>-1;)(s[r].timeline===this||e&&this._contains(s[r]))&&(a[o++]=s[r]);return n&&this._enabled(!1,!0),a},d.recent=function(){return this._recent},d._contains=function(t){for(var e=t.timeline;e;){if(e===this)return!0;e=e.timeline}return!1}
vendor: 21,482 bytes, lines 1725-1726
1725,d.shiftChildren=function(t,e,i){i=i||0;for(var s,r=this._first,n=this._labels;r;)r._startTime>=i&&(r._startTime+=t),r=r._next;if(e)for(s in n)n[s]>=i&&(n[s]+=t);return this._uncache(!0)},d._kill=function(t,e){if(!t&&!e)return this._enabled(!1,!1);for(var i=e?this.getTweensOf(e):this.getChildren(!0,!0,!1),s=i.length,r=!1;--s>-1;)i[s]._kill(t,e)&&(r=!0);return r},d.clear=function(t){var e=this.getChildren(!1,!0,!0),i=e.length;for(this._time=this._totalTime=0;--i>-1;)e[i]._enabled(!1,!1);return t!==!1&&(this._labels={}),this._uncache(!0)},d.invalidate=function(){for(var e=this._first;e;)e.invalidate(),e=e._next;return t.prototype.invalidate.call(this)},d._enabled=function(t,i){if(t===this._gc)for(var s=this._first;s;)s._enabled(t,!0),s=s._next;return e.prototype._enabled.call(this,t,i)},d.totalTime=function(){this._forcingPlayhead=!0;var e=t.prototype.totalTime.apply(this,arguments);return this._forcingPlayhead=!1,e},d.duration=function(t){return arguments.length?(0!==this.duration()&&0!==t&&this.timeScale(this._duration/t),this):(this._dirty&&this.totalDuration(),this._duration)},d.totalDuration=function(t){if(!arguments.length){if(this._dirty){for(var e,i,s=0,r=this._last,n=999999999999;r;)e=r._prev,r._dirty&&r.totalDuration(),r._startTime>n&&this._sortChildren&&!r._paused?this.add(r,r._startTime-r._delay):n=r._startTime,0>r._startTime&&!r._paused&&(s-=r._startTime,this._timeline.smoothChildTiming&&(this._startTime+=r._startTime/this._timeScale),this.shiftChildren(-r._startTime,!1,-9999999999),n=0),i=r._startTime+r._totalDuration/r._timeScale,i>s&&(s=i),r=e;this._duration=this._totalDuration=s,this._dirty=!1}return this._totalDuration}return 0!==this.totalDuration()&&0!==t&&this.timeScale(this._totalDuration/t),this},d.paused=function(e){if(!e)for(var i=this._first,s=this._time;i;)i._startTime===s&&"isPause"===i.data&&(i._rawPrevTime=s),i=i._next;return t.prototype.paused.apply(this,arguments)},d.usesFrames=function(){for(var e=this._timeline;e._timeline;)e=e._timeline;return e===t._rootFramesTimeline},d.rawTime=function(){return this._paused?this._totalTime:(this._timeline.rawTime()-this._startTime)*this._timeScale},s},!0),_gsScope._gsDefine("TimelineMax",["TimelineLite","TweenLite","easing.Ease"],function(t,e,i){var s=function(e){t.call(this,e),this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._cycle=0,this._yoyo=this.vars.yoyo===!0,this._dirty=!0},r=1e-10,n=[],a=e._internals,o=a.lazyTweens,h=a.lazyRender,l=new i(null,null,1,0),_=s.prototype=new t;return _.constructor=s,_.kill()._gc=!1,s.version="1.16.0",_.invalidate=function(){return this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._uncache(!0),t.prototype.invalidate.call(this)},_.addCallback=function(t,i,s,r){return this.add(e.delayedCall(0,t,s,r),i)},_.removeCallback=function(t,e){if(t)if(null==e)this._kill(null,t);else for(var i=this.getTweensOf(t,!1),s=i.length,r=this._parseTimeOrLabel(e);--s>-1;)i[s]._startTime===r&&i[s]._enabled(!1,!1);return this},_.removePause=function(e){return this.removeCallback(t._internals.pauseCallback,e)},_.tweenTo=function(t,i){i=i||{};var s,r,a,o={ease:l,useFrames:this.usesFrames(),immediateRender:!1};for(r in i)o[r]=i[r];return o.time=this._parseTimeOrLabel(t),s=Math.abs(Number(o.time)-this._time)/this._timeScale||.001,a=new e(this,s,o),o.onStart=function(){a.target.paused(!0),a.vars.time!==a.target.time()&&s===a.duration()&&a.duration(Math.abs(a.vars.time-a.target.time())/a.target._timeScale),i.onStart&&i.onStart.apply(i.onStartScope||a,i.onStartParams||n)},a},_.tweenFromTo=function(t,e,i){i=i||{},t=this._parseTimeOrLabel(t),i.startAt={onComplete:this.seek,onCompleteParams:[t],onCompleteScope:this},i.immediateRender=i.immediateRender!==!1;var s=this.tweenTo(e,i);return s.duration(Math.abs(s.vars.time-t)/this._timeScale||.001)},_.render=function(t,e,i){this._gc&&this._enabled(!0,!1);var s,a,l,_,u,p,f=this._dirty?this.totalDuration():this._totalDuration,c=this._duration,m=this._time,d=this._totalTime,g=this._startTime,v=this._timeScale,y=this._rawPrevTime,T=this._paused,w=this._cycle;if(t>=f)this._locked||(this._totalTime=f,this._cycle=this._repeat),this._reversed||this._hasPausedChild()||(a=!0,_="onComplete",0===this._duration&&(0===t||0>y||y===r)&&y!==t&&this._first&&(u=!0,y>r&&(_="onReverseComplete"))),this._rawPrevTime=this._duration||!e||t||this._rawPrevTime===t?t:r,this._yoyo&&0!==(1&this._cycle)?this._time=t=0:(this._time=c,t=c+1e-4);else if(1e-7>t)if(this._locked||(this._totalTime=this._cycle=0),this._time=0,(0!==m||0===c&&y!==r&&(y>0||0>t&&y>=0)&&!this._locked)&&(_="onReverseComplete",a=this._reversed),0>t)this._active=!1,this._timeline.autoRemoveChildren&&this._reversed?(u=a=!0,_="onReverseComplete"):y>=0&&this._first&&(u=!0),this._rawPrevTime=t;else{if(this._rawPrevTime=c||!e||t||this._rawPrevTime===t?t:r,0===t&&a)for(s=this._first;s&&0===s._startTime;)s._duration||(a=!1),s=s._next;t=0,this._initted||(u=!0)}else 0===c&&0>y&&(u=!0),this._time=this._rawPrevTime=t,this._locked||(this._totalTime=t,0!==this._repeat&&(p=c+this._repeatDelay,this._cycle=this._totalTime/p>>0,0!==this._cycle&&this._cycle===this._totalTime/p&&this._cycle--,this._time=this._totalTime-this._cycle*p,this._yoyo&&0!==(1&this._cycle)&&(this._time=c-this._time),this._time>c?(this._time=c,t=c+1e-4):0>this._time?this._time=t=0:t=this._time));if(this._cycle!==w&&!this._locked){var x=this._yoyo&&0!==(1&w),b=x===(this._yoyo&&0!==(1&this._cycle)),P=this._totalTime,S=this._cycle,k=this._rawPrevTime,R=this._time;if(this._totalTime=w*c,w>this._cycle?x=!x:this._totalTime+=c,this._time=m,this._rawPrevTime=0===c?y-1e-4:y,this._cycle=w,this._locked=!0,m=x?0:c,this.render(m,e,0===c),e||this._gc||this.vars.onRepeat&&this.vars.onRepeat.apply(this.vars.onRepeatScope||this,this.vars.onRepeatParams||n),b&&(m=x?c+1e-4:-1e-4,this.render(m,!0,!1)),this._locked=!1,this._paused&&!T)return;this._time=R,this._totalTime=P,this._cycle=S,this._rawPrevTime=k}if(!(this._time!==m&&this._first||i||u))return d!==this._totalTime&&this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||n)),void 0;if(this._initted||(this._initted=!0),this._active||!this._paused&&this._totalTime!==d&&t>0&&(this._active=!0),0===d&&this.vars.onStart&&0!==this._totalTime&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||n)),this._time>=m)for(s=this._first;s&&(l=s._next,!this._paused||T);)(s._active||s._startTime<=this._time&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=l;else for(s=this._last;s&&(l=s._prev,!this._paused||T);)(s._active||m>=s._startTime&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=l;this._onUpdate&&(e||(o.length&&h(),this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||n))),_&&(this._locked||this._gc||(g===this._startTime||v!==this._timeScale)&&(0===this._time||f>=this.totalDuration())&&(a&&(o.length&&h(),this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[_]&&this.vars[_].apply(this.vars[_+"Scope"]||this,this.vars[_+"Params"]||n)))},_.getActive=function(t,e,i){null==t&&(t=!0),null==e&&(e=!0),null==i&&(i=!1);var s,r,n=[],a=this.getChildren(t,e,i),o=0,h=a.length;for(s=0;h>s;s++)r=a[s],r.isActive()&&(n[o++]=r);return n},_.getLabelAfter=function(t){t||0!==t&&(t=this._time);var e,i=this.getLabelsArray(),s=i.length;for(e=0;s>e;e++)if(i[e].time>t)return i[e].name;return null},_.getLabelBefore=function(t){null==t&&(t=this._time);for(var e=this.getLabelsArray(),i=e.length;--i>-1;)if(t>e[i].time)return e[i].name;return null},_.getLabelsArray=function(){var t,e=[],i=0;for(t in this._labels)e[i++]={time:this._labels[t],name:t};return e.sort(function(t,e){return t.time-e.time}),e},_.progress=function(t,e){return arguments.length?this.totalTime(this.duration()*(this._yoyo&&0!==(1&this._cycle)?1-t:t)+this._cycle*(this._duration+this._repeatDelay),e):this._time/this.duration()},_.totalProgress=function(t,e){return arguments.length?this.totalTime(this.totalDuration()*t,e):this._totalTime/this.totalDuration()},_.totalDuration=function(e){return arguments.length?-1===this._repeat?this:this.duration((e-this._repeat*this._repeatDelay)/(this._repeat+1)):(this._dirty&&(t.prototype.totalDuration.call(this),this._totalDuration=-1===this._repeat?999999999999:this._duration*(this._repeat+1)+this._repeatDelay*this._repeat),this._totalDuration)},_.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),t>this._duration&&(t=this._duration),this._yoyo&&0!==(1&this._cycle)?t=this._duration-t+this._cycle*(this._duration+this._repeatDelay):0!==this._repeat&&(t+=this._cycle*(this._duration+this._repeatDelay)),this.totalTime(t,e)):this._time},_.repeat=function(t){return arguments.length?(this._repeat=t,this._uncache(!0)):this._repeat},_.repeatDelay=function(t){return arguments.length?(this._repeatDelay=t,this._uncache(!0)):this._repeatDelay},_.yoyo=function(t){return arguments.length?(this._yoyo=t,this):this._yoyo},_.currentLabel=function(t){return arguments.length?this.seek(t,!0):this.getLabelBefore(this._time+1e-8)},s},!0),function(){var t=180/Math.PI,e=[],i=[],s=[],r={},n=_gsScope._gsDefine.globals,a=function(t,e,i,s){this.a=t,this.b=e,this.c=i,this.d=s,this.da=s-t,this.ca=i-t,this.ba=e-t},o=",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",h=function(t,e,i,s){var r={a:t},n={},a={},o={c:s},h=(t+e)/2,l=(e+i)/2,_=(i+s)/2,u=(h+l)/2,p=(l+_)/2,f=(p-u)/8;return r.b=h+(t-h)/4,n.b=u+f,r.c=n.a=(r.b+n.b)/2,n.c=a.a=(u+p)/2,a.b=p-f,o.b=_+(s-_)/4,a.c=o.a=(a.b+o.b)/2,[r,n,a,o]},l=function(t,r,n,a,o){var l,_,u,p,f,c,m,d,g,v,y,T,w,x=t.length-1,b=0,P=t[0].a;for(l=0;x>l;l++)f=t[b],_=f.a,u=f.d,p=t[b+1].d,o?(y=e[l],T=i[l],w=.25*(T+y)*r/(a?.5:s[l]||.5),c=u-(u-_)*(a?.5*r:0!==y?w/y:0),m=u+(p-u)*(a?.5*r:0!==T?w/T:0),d=u-(c+((m-c)*(3*y/(y+T)+.5)/4||0))):(c=u-.5*(u-_)*r,m=u+.5*(p-u)*r,d=u-(c+m)/2),c+=d,m+=d,f.c=g=c,f.b=0!==l?P:P=f.a+.6*(f.c-f.a),f.da=u-_,f.ca=g-_,f.ba=P-_,n?(v=h(_,P,g,u),t.splice(b,1,v[0],v[1],v[2],v[3]),b+=4):b++,P=m;f=t[b],f.b=P,f.c=P+.4*(f.d-P),f.da=f.d-f.a,f.ca=f.c-f.a,f.ba=P-f.a,n&&(v=h(f.a,P,f.c,f.d),t.splice(b,1,v[0],v[1],v[2],v[3]))},_=function(t,s,r,n){var o,h,l,_,u,p,f=[];if(n)for(t=[n].concat(t),h=t.length;--h>-1;)"string"==typeof(p=t[h][s])&&"="===p.charAt(1)&&(t[h][s]=n[s]+Number(p.charAt(0)+p.substr(2)));if(o=t.length-2,0>o)return f[0]=new a(t[0][s],0,0,t[-1>o?0:1][s]),f;for(h=0;o>h;h++)l=t[h][s],_=t[h+1][s],f[h]=new a(l,0,0,_),r&&(u=t[h+2][s],e[h]=(e[h]||0)+(_-l)*(_-l),i[h]=(i[h]||0)+(u-_)*(u-_));return f[h]=new a(t[h][s],0,0,t[h+1][s]),f},u=function(t,n,a,h,u,p){var f,c,m,d,g,v,y,T,w={},x=[],b=p||t[0];u="string"==typeof u?","+u+",":o,null==n&&(n=1);for(c in t[0])x.push(c);if(t.length>1){for(T=t[t.length-1],y=!0,f=x.length;--f>-1;)if(c=x[f],Math.abs(b[c]-T[c])>.05){y=!1;break}y&&(t=t.concat(),p&&t.unshift(p),t.push(t[1]),p=t[t.length-3])}for(e.length=i.length=s.length=0,f=x.length;--f>-1;)c=x[f],r[c]=-1!==u.indexOf(","+c+","),w[c]=_(t,c,r[c],p);for(f=e.length;--f>-1;)e[f]=Math.sqrt(e[f]),i[f]=Math.sqrt(i[f]);if(!h){for(f=x.length;--f>-1;)if(r[c])for(m=w[x[f]],v=m.length-1,d=0;v>d;d++)g=m[d+1].da/i[d]+m[d].da/e[d],s[d]=(s[d]||0)+g*g;for(f=s.length;--f>-1;)s[f]=Math.sqrt(s[f])}for(f=x.length,d=a?4:1;--f>-1;)c=x[f],m=w[c],l(m,n,a,h,r[c]),y&&(m.splice(0,d),m.splice(m.length-d,d));return w},p=function(t,e,i){e=e||"soft";var s,r,n,o,h,l,_,u,p,f,c,m={},d="cubic"===e?3:2,g="soft"===e,v=[];if(g&&i&&(t=[i].concat(t)),null==t||d+1>t.length)throw"invalid Bezier data";for(p in t[0])v.push(p);for(l=v.length;--l>-1;){for(p=v[l],m[p]=h=[],f=0,u=t.length,_=0;u>_;_++)s=null==i?t[_][p]:"string"==typeof(c=t[_][p])&&"="===c.charAt(1)?i[p]+Number(c.charAt(0)+c.substr(2)):Number(c),g&&_>1&&u-1>_&&(h[f++]=(s+h[f-2])/2),h[f++]=s;for(u=f-d+1,f=0,_=0;u>_;_+=d)s=h[_],r=h[_+1],n=h[_+2],o=2===d?0:h[_+3],h[f++]=c=3===d?new a(s,r,n,o):new a(s,(2*r+s)/3,(2*r+n)/3,n);h.length=f}return m},f=function(t,e,i){for(var s,r,n,a,o,h,l,_,u,p,f,c=1/i,m=t.length;--m>-1;)for(p=t[m],n=p.a,a=p.d-n,o=p.c-n,h=p.b-n,s=r=0,_=1;i>=_;_++)l=c*_,u=1-l,s=r-(r=(l*l*a+3*u*(l*o+u*h))*l),f=m*i+_-1,e[f]=(e[f]||0)+s*s},c=function(t,e){e=e>>0||6;var i,s,r,n,a=[],o=[],h=0,l=0,_=e-1,u=[],p=[];for(i in t)f(t[i],a,e);for(r=a.length,s=0;r>s;s++)h+=Math.sqrt(a[s]),n=s%e,p[n]=h,n===_&&(l+=h,n=s/e>>0,u[n]=p,o[n]=l,h=0,p=[]);return{length:l,lengths:o,segments:u}},m=_gsScope._gsDefine.plugin({propName:"bezier",priority:-1,version:"1.3.4",API:2,global:!0,init:function(t,e,i){this._target=t,e instanceof Array&&(e={values:e}),this._func={},this._round={},this._props=[],this._timeRes=null==e.timeResolution?6:parseInt(e.timeResolution,10);var s,r,n,a,o,h=e.values||[],l={},_=h[0],f=e.autoRotate||i.vars.orientToBezier;this._autoRotate=f?f instanceof Array?f:[["x","y","rotation",f===!0?0:Number(f)||0]]:null;for(s in _)this._props.push(s);for(n=this._props.length;--n>-1;)s=this._props[n],this._overwriteProps.push(s),r=this._func[s]="function"==typeof t[s],l[s]=r?t[s.indexOf("set")||"function"!=typeof t["get"+s.substr(3)]?s:"get"+s.substr(3)]():parseFloat(t[s]),o||l[s]!==h[0][s]&&(o=l);if(this._beziers="cubic"!==e.type&&"quadratic"!==e.type&&"soft"!==e.type?u(h,isNaN(e.curviness)?1:e.curviness,!1,"thruBasic"===e.type,e.correlate,o):p(h,e.type,l),this._segCount=this._beziers[s].length,this._timeRes){var m=c(this._beziers,this._timeRes);this._length=m.length,this._lengths=m.lengths,this._segments=m.segments,this._l1=this._li=this._s1=this._si=0,this._l2=this._lengths[0],this._curSeg=this._segments[0],this._s2=this._curSeg[0],this._prec=1/this._curSeg.length}if(f=this._autoRotate)for(this._initialRotations=[],f[0]instanceof Array||(this._autoRotate=f=[f]),n=f.length;--n>-1;){for(a=0;3>a;a++)s=f[n][a],this._func[s]="function"==typeof t[s]?t[s.indexOf("set")||"function"!=typeof t["get"+s.substr(3)]?s:"get"+s.substr(3)]:!1; 1726s=f[n][2],this._initialRotations[n]=this._func[s]?this._func[s].call(this._target):this._target[s]}return this._startRatio=i.vars.runBackwards?1:0,!0},set:function(e){var i,s,r,n,a,o,h,l,_,u,p=this._segCount,f=this._func,c=this._target,m=e!==this._startRatio;if(this._timeRes){if(_=this._lengths,u=this._curSeg,e*=this._length,r=this._li,e>this._l2&&p-1>r){for(l=p-1;l>r&&e>=(this._l2=_[++r]););this._l1=_[r-1],this._li=r,this._curSeg=u=this._segments[r],this._s2=u[this._s1=this._si=0]}else if(this._l1>e&&r>0){for(;r>0&&(this._l1=_[--r])>=e;);0===r&&this._l1>e?this._l1=0:r++,this._l2=_[r],this._li=r,this._curSeg=u=this._segments[r],this._s1=u[(this._si=u.length-1)-1]||0,this._s2=u[this._si]}if(i=r,e-=this._l1,r=this._si,e>this._s2&&u.length-1>r){for(l=u.length-1;l>r&&e>=(this._s2=u[++r]););this._s1=u[r-1],this._si=r}else if(this._s1>e&&r>0){for(;r>0&&(this._s1=u[--r])>=e;);0===r&&this._s1>e?this._s1=0:r++,this._s2=u[r],this._si=r}o=(r+(e-this._s1)/(this._s2-this._s1))*this._prec}else i=0>e?0:e>=1?p-1:p*e>>0,o=(e-i*(1/p))*p;for(s=1-o,r=this._props.length;--r>-1;)n=this._props[r],a=this._beziers[n][i],h=(o*o*a.da+3*s*(o*a.ca+s*a.ba))*o+a.a,this._round[n]&&(h=Math.round(h)),f[n]?c[n](h):c[n]=h;if(this._autoRotate){var d,g,v,y,T,w,x,b=this._autoRotate;for(r=b.length;--r>-1;)n=b[r][2],w=b[r][3]||0,x=b[r][4]===!0?1:t,a=this._beziers[b[r][0]],d=this._beziers[b[r][1]],a&&d&&(a=a[i],d=d[i],g=a.a+(a.b-a.a)*o,y=a.b+(a.c-a.b)*o,g+=(y-g)*o,y+=(a.c+(a.d-a.c)*o-y)*o,v=d.a+(d.b-d.a)*o,T=d.b+(d.c-d.b)*o,v+=(T-v)*o,T+=(d.c+(d.d-d.c)*o-T)*o,h=m?Math.atan2(T-v,y-g)*x+w:this._initialRotations[r],f[n]?c[n](h):c[n]=h)}}}),d=m.prototype;m.bezierThrough=u,m.cubicToQuadratic=h,m._autoCSS=!0,m.quadraticToCubic=function(t,e,i){return new a(t,(2*e+t)/3,(2*e+i)/3,i)},m._cssRegister=function(){var t=n.CSSPlugin;if(t){var e=t._internals,i=e._parseToProxy,s=e._setPluginRatio,r=e.CSSPropTween;e._registerComplexSpecialProp("bezier",{parser:function(t,e,n,a,o,h){e instanceof Array&&(e={values:e}),h=new m;var l,_,u,p=e.values,f=p.length-1,c=[],d={};if(0>f)return o;for(l=0;f>=l;l++)u=i(t,p[l],a,o,h,f!==l),c[l]=u.end;for(_ in e)d[_]=e[_];return d.values=c,o=new r(t,"bezier",0,0,u.pt,2),o.data=u,o.plugin=h,o.setRatio=s,0===d.autoRotate&&(d.autoRotate=!0),!d.autoRotate||d.autoRotate instanceof Array||(l=d.autoRotate===!0?0:Number(d.autoRotate),d.autoRotate=null!=u.end.left?[["left","top","rotation",l,!1]]:null!=u.end.x?[["x","y","rotation",l,!1]]:!1),d.autoRotate&&(a._transform||a._enableTransforms(!1),u.autoRotate=a._target._gsTransform),h._onInitTween(u.proxy,d,a._tween),o}})}},d._roundProps=function(t,e){for(var i=this._overwriteProps,s=i.length;--s>-1;)(t[i[s]]||t.bezier||t.bezierThrough)&&(this._round[i[s]]=e)},d._kill=function(t){var e,i,s=this._props;for(e in this._beziers)if(e in t)for(delete this._beziers[e],delete this._func[e],i=s.length;--i>-1;)s[i]===e&&s.splice(i,1);return this._super._kill.call(this,t)}}(),_gsScope._gsDefine("plugins.CSSPlugin",["plugins.TweenPlugin","TweenLite"],function(t,e){var i,s,r,n,a=function(){t.call(this,"css"),this._overwriteProps.length=0,this.setRatio=a.prototype.setRatio},o=_gsScope._gsDefine.globals,h={},l=a.prototype=new t("css");l.constructor=a,a.version="1.16.0",a.API=2,a.defaultTransformPerspective=0,a.defaultSkewType="compensated",l="px",a.suffixMap={top:l,right:l,bottom:l,left:l,width:l,height:l,fontSize:l,padding:l,margin:l,perspective:l,lineHeight:""};var _,u,p,f,c,m,d=/(?:\d|\-\d|\.\d|\-\.\d)+/g,g=/(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,v=/(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi,y=/(?![+-]?\d*\.?\d+|[+-]|e[+-]\d+)[^0-9]/g,T=/(?:\d|\-|\+|=|#|\.)*/g,w=/opacity *= *([^)]*)/i,x=/opacity:([^;]*)/i,b=/alpha\(opacity *=.+?\)/i,P=/^(rgb|hsl)/,S=/([A-Z])/g,k=/-([a-z])/gi,R=/(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi,A=function(t,e){return e.toUpperCase()},O=/(?:Left|Right|Width)/i,C=/(M11|M12|M21|M22)=[\d\-\.e]+/gi,D=/progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,M=/,(?=[^\)]*(?:\(|$))/gi,z=Math.PI/180,I=180/Math.PI,F={},E=document,N=function(t){return E.createElementNS?E.createElementNS("http://www.w3.org/1999/xhtml",t):E.createElement(t)},L=N("div"),X=N("img"),U=a._internals={_specialProps:h},Y=navigator.userAgent,j=function(){var t=Y.indexOf("Android"),e=N("a");return p=-1!==Y.indexOf("Safari")&&-1===Y.indexOf("Chrome")&&(-1===t||Number(Y.substr(t+8,1))>3),c=p&&6>Number(Y.substr(Y.indexOf("Version/")+8,1)),f=-1!==Y.indexOf("Firefox"),(/MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(Y)||/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(Y))&&(m=parseFloat(RegExp.$1)),e?(e.style.cssText="top:1px;opacity:.55;",/^0.55/.test(e.style.opacity)):!1}(),B=function(t){return w.test("string"==typeof t?t:(t.currentStyle?t.currentStyle.filter:t.style.filter)||"")?parseFloat(RegExp.$1)/100:1},q=function(t){window.console&&console.log(t)},V="",G="",W=function(t,e){e=e||L;var i,s,r=e.style;if(void 0!==r[t])return t;for(t=t.charAt(0).toUpperCase()+t.substr(1),i=["O","Moz","ms","Ms","Webkit"],s=5;--s>-1&&void 0===r[i[s]+t];);return s>=0?(G=3===s?"ms":i[s],V="-"+G.toLowerCase()+"-",G+t):null},Z=E.defaultView?E.defaultView.getComputedStyle:function(){},Q=a.getStyle=function(t,e,i,s,r){var n;return j||"opacity"!==e?(!s&&t.style[e]?n=t.style[e]:(i=i||Z(t))?n=i[e]||i.getPropertyValue(e)||i.getPropertyValue(e.replace(S,"-$1").toLowerCase()):t.currentStyle&&(n=t.currentStyle[e]),null==r||n&&"none"!==n&&"auto"!==n&&"auto auto"!==n?n:r):B(t)},$=U.convertToPixels=function(t,i,s,r,n){if("px"===r||!r)return s;if("auto"===r||!s)return 0;var o,h,l,_=O.test(i),u=t,p=L.style,f=0>s;if(f&&(s=-s),"%"===r&&-1!==i.indexOf("border"))o=s/100*(_?t.clientWidth:t.clientHeight);else{if(p.cssText="border:0 solid red;position:"+Q(t,"position")+";line-height:0;","%"!==r&&u.appendChild)p[_?"borderLeftWidth":"borderTopWidth"]=s+r;else{if(u=t.parentNode||E.body,h=u._gsCache,l=e.ticker.frame,h&&_&&h.time===l)return h.width*s/100;p[_?"width":"height"]=s+r}u.appendChild(L),o=parseFloat(L[_?"offsetWidth":"offsetHeight"]),u.removeChild(L),_&&"%"===r&&a.cacheWidths!==!1&&(h=u._gsCache=u._gsCache||{},h.time=l,h.width=100*(o/s)),0!==o||n||(o=$(t,i,s,r,!0))}return f?-o:o},H=U.calculateOffset=function(t,e,i){if("absolute"!==Q(t,"position",i))return 0;var s="left"===e?"Left":"Top",r=Q(t,"margin"+s,i);return t["offset"+s]-($(t,e,parseFloat(r),r.replace(T,""))||0)},K=function(t,e){var i,s,r,n={};if(e=e||Z(t,null))if(i=e.length)for(;--i>-1;)r=e[i],(-1===r.indexOf("-transform")||be===r)&&(n[r.replace(k,A)]=e.getPropertyValue(r));else for(i in e)(-1===i.indexOf("Transform")||xe===i)&&(n[i]=e[i]);else if(e=t.currentStyle||t.style)for(i in e)"string"==typeof i&&void 0===n[i]&&(n[i.replace(k,A)]=e[i]);return j||(n.opacity=B(t)),s=Me(t,e,!1),n.rotation=s.rotation,n.skewX=s.skewX,n.scaleX=s.scaleX,n.scaleY=s.scaleY,n.x=s.x,n.y=s.y,Se&&(n.z=s.z,n.rotationX=s.rotationX,n.rotationY=s.rotationY,n.scaleZ=s.scaleZ),n.filters&&delete n.filters,n},J=function(t,e,i,s,r){var n,a,o,h={},l=t.style;for(a in i)"cssText"!==a&&"length"!==a&&isNaN(a)&&(e[a]!==(n=i[a])||r&&r[a])&&-1===a.indexOf("Origin")&&("number"==typeof n||"string"==typeof n)&&(h[a]="auto"!==n||"left"!==a&&"top"!==a?""!==n&&"auto"!==n&&"none"!==n||"string"!=typeof e[a]||""===e[a].replace(y,"")?n:0:H(t,a),void 0!==l[a]&&(o=new fe(l,a,l[a
1726],o)));if(s)for(a in s)"className"!==a&&(h[a]=s[a]);return{difs:h,firstMPT:o}},te={width:["Left","Right"],height:["Top","Bottom"]},ee=["marginLeft","marginRight","marginTop","marginBottom"],ie=function(t,e,i){var s=parseFloat("width"===e?t.offsetWidth:t.offsetHeight),r=te[e],n=r.length;for(i=i||Z(t,null);--n>-1;)s-=parseFloat(Q(t,"padding"+r[n],i,!0))||0,s-=parseFloat(Q(t,"border"+r[n]+"Width",i,!0))||0;return s},se=function(t,e){(null==t||""===t||"auto"===t||"auto auto"===t)&&(t="0 0");var i=t.split(" "),s=-1!==t.indexOf("left")?"0%":-1!==t.indexOf("right")?"100%":i[0],r=-1!==t.indexOf("top")?"0%":-1!==t.indexOf("bottom")?"100%":i[1];return null==r?r="center"===s?"50%":"0":"center"===r&&(r="50%"),("center"===s||isNaN(parseFloat(s))&&-1===(s+"").indexOf("="))&&(s="50%"),e&&(e.oxp=-1!==s.indexOf("%"),e.oyp=-1!==r.indexOf("%"),e.oxr="="===s.charAt(1),e.oyr="="===r.charAt(1),e.ox=parseFloat(s.replace(y,"")),e.oy=parseFloat(r.replace(y,""))),s+" "+r+(i.length>2?" "+i[2]:"")},re=function(t,e){return"string"==typeof t&&"="===t.charAt(1)?parseInt(t.charAt(0)+"1",10)*parseFloat(t.substr(2)):parseFloat(t)-parseFloat(e)},ne=function(t,e){return null==t?e:"string"==typeof t&&"="===t.charAt(1)?parseInt(t.charAt(0)+"1",10)*parseFloat(t.substr(2))+e:parseFloat(t)},ae=function(t,e,i,s){var r,n,a,o,h,l=1e-6;return null==t?o=e:"number"==typeof t?o=t:(r=360,n=t.split("_"),h="="===t.charAt(1),a=(h?parseInt(t.charAt(0)+"1",10)*parseFloat(n[0].substr(2)):parseFloat(n[0]))*(-1===t.indexOf("rad")?1:I)-(h?0:e),n.length&&(s&&(s[i]=e+a),-1!==t.indexOf("short")&&(a%=r,a!==a%(r/2)&&(a=0>a?a+r:a-r)),-1!==t.indexOf("_cw")&&0>a?a=(a+9999999999*r)%r-(0|a/r)*r:-1!==t.indexOf("ccw")&&a>0&&(a=(a-9999999999*r)%r-(0|a/r)*r)),o=e+a),l>o&&o>-l&&(o=0),o},oe={aqua:[0,255,255],lime:[0,255,0],silver:[192,192,192],black:[0,0,0],maroon:[128,0,0],teal:[0,128,128],blue:[0,0,255],navy:[0,0,128],white:[255,255,255],fuchsia:[255,0,255],olive:[128,128,0],yellow:[255,255,0],orange:[255,165,0],gray:[128,128,128],purple:[128,0,128],green:[0,128,0],red:[255,0,0],pink:[255,192,203],cyan:[0,255,255],transparent:[255,255,255,0]},he=function(t,e,i){return t=0>t?t+1:t>1?t-1:t,0|255*(1>6*t?e+6*(i-e)*t:.5>t?i:2>3*t?e+6*(i-e)*(2/3-t):e)+.5},le=a.parseColor=function(t){var e,i,s,r,n,a;return t&&""!==t?"number"==typeof t?[t>>16,255&t>>8,255&t]:(","===t.charAt(t.length-1)&&(t=t.substr(0,t.length-1)),oe[t]?oe[t]:"#"===t.charAt(0)?(4===t.length&&(e=t.charAt(1),i=t.charAt(2),s=t.charAt(3),t="#"+e+e+i+i+s+s),t=parseInt(t.substr(1),16),[t>>16,255&t>>8,255&t]):"hsl"===t.substr(0,3)?(t=t.match(d),r=Number(t[0])%360/360,n=Number(t[1])/100,a=Number(t[2])/100,i=.5>=a?a*(n+1):a+n-a*n,e=2*a-i,t.length>3&&(t[3]=Number(t[3])),t[0]=he(r+1/3,e,i),t[1]=he(r,e,i),t[2]=he(r-1/3,e,i),t):(t=t.match(d)||oe.transparent,t[0]=Number(t[0]),t[1]=Number(t[1]),t[2]=Number(t[2]),t.length>3&&(t[3]=Number(t[3])),t)):oe.black},_e="(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#.+?\\b";for(l in oe)_e+="|"+l+"\\b";_e=RegExp(_e+")","gi");var ue=function(t,e,i,s){if(null==t)return function(t){return t};var r,n=e?(t.match(_e)||[""])[0]:"",a=t.split(n).join("").match(v)||[],o=t.substr(0,t.indexOf(a[0])),h=")"===t.charAt(t.length-1)?")":"",l=-1!==t.indexOf(" ")?" ":",",_=a.length,u=_>0?a[0].replace(d,""):"";return _?r=e?function(t){var e,p,f,c;if("number"==typeof t)t+=u;else if(s&&M.test(t)){for(c=t.replace(M,"|").split("|"),f=0;c.length>f;f++)c[f]=r(c[f]);return c.join(",")}if(e=(t.match(_e)||[n])[0],p=t.split(e).join("").match(v)||[],f=p.length,_>f--)for(;_>++f;)p[f]=i?p[0|(f-1)/2]:a[f];return o+p.join(l)+l+e+h+(-1!==t.indexOf("inset")?" inset":"")}:function(t){var e,n,p;if("number"==typeof t)t+=u;else if(s&&M.test(t)){for(n=t.replace(M,"|").split("|"),p=0;n.length>p;p++)n[p]=r(n[p]);return n.join(",")}if(e=t.match(v)||[],p=e.length,_>p--)for(;_>++p;)e[p]=i?e[0|(p-1)/2]:a[p];return o+e.join(l)+h}:function(t){return t}},pe=function(t){return t=t.split(","),function(e,i,s,r,n,a,o){var h,l=(i+"").split(" ");for(o={},h=0;4>h;h++)o[t[h]]=l[h]=l[h]||l[(h-1)/2>>0];return r.parse(e,o,n,a)}},fe=(U._setPluginRatio=function(t){this.plugin.setRatio(t);for(var e,i,s,r,n=this.data,a=n.proxy,o=n.firstMPT,h=1e-6;o;
vendor: 4,282 bytes, line 1726
1726)e=a[o.v],o.r?e=Math.round(e):h>e&&e>-h&&(e=0),o.t[o.p]=e,o=o._next;if(n.autoRotate&&(n.autoRotate.rotation=a.rotation),1===t)for(o=n.firstMPT;o;){if(i=o.t,i.type){if(1===i.type){for(r=i.xs0+i.s+i.xs1,s=1;i.l>s;s++)r+=i["xn"+s]+i["xs"+(s+1)];i.e=r}}else i.e=i.s+i.xs0;o=o._next}},function(t,e,i,s,r){this.t=t,this.p=e,this.v=i,this.r=r,s&&(s._prev=this,this._next=s)}),ce=(U._parseToProxy=function(t,e,i,s,r,n){var a,o,h,l,_,u=s,p={},f={},c=i._transform,m=F;for(i._transform=null,F=e,s=_=i.parse(t,e,s,r),F=m,n&&(i._transform=c,u&&(u._prev=null,u._prev&&(u._prev._next=null)));s&&s!==u;){if(1>=s.type&&(o=s.p,f[o]=s.s+s.c,p[o]=s.s,n||(l=new fe(s,"s",o,l,s.r),s.c=0),1===s.type))for(a=s.l;--a>0;)h="xn"+a,o=s.p+"_"+h,f[o]=s.data[h],p[o]=s[h],n||(l=new fe(s,h,o,l,s.rxp[h]));s=s._next}return{proxy:p,end:f,firstMPT:l,pt:_}},U.CSSPropTween=function(t,e,s,r,a,o,h,l,_,u,p){this.t=t,this.p=e,this.s=s,this.c=r,this.n=h||e,t instanceof ce||n.push(this.n),this.r=l,this.type=o||0,_&&(this.pr=_,i=!0),this.b=void 0===u?s:u,this.e=void 0===p?s+r:p,a&&(this._next=a,a._prev=this)}),me=a.parseComplex=function(t,e,i,s,r,n,a,o,h,l){i=i||n||"",a=new ce(t,e,0,0,a,l?2:1,null,!1,o,i,s),s+="";var u,p,f,c,m,v,y,T,w,x,b,S,k=i.split(", ").join(",").split(" "),R=s.split(", ").join(",").split(" "),A=k.length,O=_!==!1;for((-1!==s.indexOf(",")||-1!==i.indexOf(","))&&(k=k.join(" ").replace(M,", ").split(" "),R=R.join(" ").replace(M,", ").split(" "),A=k.length),A!==R.length&&(k=(n||"").split(" "),A=k.length),a.plugin=h,a.setRatio=l,u=0;A>u;u++)if(c=k[u],m=R[u],T=parseFloat(c),T||0===T)a.appendXtra("",T,re(m,T),m.replace(g,""),O&&-1!==m.indexOf("px"),!0);else if(r&&("#"===c.charAt(0)||oe[c]||P.test(c)))S=","===m.charAt(m.length-1)?"),":")",c=le(c),m=le(m),w=c.length+m.length>6,w&&!j&&0===m[3]?(a["xs"+a.l]+=a.l?" transparent":"transparent",a.e=a.e.split(R[u]).join("transparent")):(j||(w=!1),a.appendXtra(w?"rgba(":"rgb(",c[0],m[0]-c[0],",",!0,!0).appendXtra("",c[1],m[1]-c[1],",",!0).appendXtra("",c[2],m[2]-c[2],w?",":S,!0),w&&(c=4>c.length?1:c[3],a.appendXtra("",c,(4>m.length?1:m[3])-c,S,!1)));else if(v=c.match(d)){if(y=m.match(g),!y||y.length!==v.length)return a;for(f=0,p=0;v.length>p;p++)b=v[p],x=c.indexOf(b,f),a.appendXtra(c.substr(f,x-f),Number(b),re(y[p],b),"",O&&"px"===c.substr(x+b.length,2),0===p),f=x+b.length;a["xs"+a.l]+=c.substr(f)}else a["xs"+a.l]+=a.l?" "+c:c;if(-1!==s.indexOf("=")&&a.data){for(S=a.xs0+a.data.s,u=1;a.l>u;u++)S+=a["xs"+u]+a.data["xn"+u];a.e=S+a["xs"+u]}return a.l||(a.type=-1,a.xs0=a.e),a.xfirst||a},de=9;for(l=ce.prototype,l.l=l.pr=0;--de>0;)l["xn"+de]=0,l["xs"+de]="";l.xs0="",l._next=l._prev=l.xfirst=l.data=l.plugin=l.setRatio=l.rxp=null,l.appendXtra=function(t,e,i,s,r,n){var a=this,o=a.l;return a["xs"+o]+=n&&o?" "+t:t||"",i||0===o||a.plugin?(a.l++,a.type=a.setRatio?2:1,a["xs"+a.l]=s||"",o>0?(a.data["xn"+o]=e+i,a.rxp["xn"+o]=r,a["xn"+o]=e,a.plugin||(a.xfirst=new ce(a,"xn"+o,e,i,a.xfirst||a,0,a.n,r,a.pr),a.xfirst.xs0=0),a):(a.data={s:e+i},a.rxp={},a.s=e,a.c=i,a.r=r,a)):(a["xs"+o]+=e+(s||""),a)};var ge=function(t,e){e=e||{},this.p=e.prefix?W(t)||t:t,h[t]=h[this.p]=this,this.format=e.formatter||ue(e.defaultValue,e.color,e.collapsible,e.multi),e.parser&&(this.parse=e.parser),this.clrs=e.color,this.multi=e.multi,this.keyword=e.keyword,this.dflt=e.defaultValue,this.pr=e.priority||0},ve=U._registerComplexSpecialProp=function(t,e,i){"object"!=typeof e&&(e={parser:i});var s,r,n=t.split(","),a=e.defaultValue;for(i=i||[a],s=0;n.length>s;s++)e.prefix=0===s&&e.prefix,e.defaultValue=i[s]||a,r=new ge(n[s],e)},ye=function(t){if(!h[t]){var e=t.charAt(0).toUpperCase()+t.substr(1)+"Plugin";ve(t,{parser:function(t,i,s,r,n,a,l){var _=o.com.greensock.plugins[e];return _?(_._cssRegister(),h[s].parse(t,i,s,r,n,a,l)):(q("Error: "+e+" js file not loaded."),n)}})}};l=ge.prototype,l.parseComplex=function(t,e,i,s,r,n){var a,o,h,l,_,u,p=this.keyword;if(this.multi&&(M.test(i)||M.test(e)?(o=e.replace(M,"|").split("|"),h=i.replace(M,"|").split("|")):p&&(o=[e],h=[i])),h){for(l=h.length>o.length?h.length:o.length,a=0;l>a;a++)e=o[a]=o[a]||this.dflt,i=h[a]=h[a]||this.dflt,p&&(_=e.indexOf(p),u=i.indexOf(p),_!==u&&(-1===u?o[a]=o[a].split(p).join(""):-1===_&&(o[a]+=" "+p)));e=o.join(", "),i=h.join(", ")}return me(t,this.p,e,i,this.clrs,this.dflt,s,this.pr,r,n)}
1726,l.parse=function(t,e,i,s,n,a){return this.parseComplex(t.style,this.format(Q(t,this.p,r,!1,this.dflt)),this.format(e),n,a)},a.registerSpecialProp=function(t,e,i){ve(t,{parser:function(t,s,r,n,a,o){var h=new ce(t,r,0,0,a,2,r,!1,i);return h.plugin=o,h.setRatio=e(t,s,n._tween,r),h},priority:i})},a.useSVGTransformAttr=p;var Te,we="scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective,xPercent,yPercent".split(","),xe=W("transform"),be=V+"transform",Pe=W("transformOrigin"),Se=null!==W("perspective"),ke=U.Transform=function(){this.perspective=parseFloat(a.defaultTransformPerspective)||0,this.force3D=a.defaultForce3D!==!1&&Se?a.defaultForce3D||"auto":!1},Re=window.SVGElement,Ae=function(t,e,i){var s,r=E.createElementNS("http://www.w3.org/2000/svg",t),n=/([a-z])([A-Z])/g;for(s in i)r.setAttributeNS(null,s.replace(n,"$1-$2").toLowerCase(),i[s]);return e.appendChild(r),r},Oe=E.documentElement,Ce=function(){var t,e,i,s=m||/Android/i.test(Y)&&!window.chrome;return E.createElementNS&&!s&&(t=Ae("svg",Oe),e=Ae("rect",t,{width:100,height:50,x:100}),i=e.getBoundingClientRect().width,e.style[Pe]="50% 50%",e.style[xe]="scaleX(0.5)",s=i===e.getBoundingClientRect().width&&!(f&&Se),Oe.removeChild(t)),s}(),De=function(t,e,i,s){var r,n;s&&(n=s.split(" ")).length||(r=t.getBBox(),e=se(e).split(" "),n=[(-1!==e[0].indexOf("%")?parseFloat(e[0])/100*r.width:parseFloat(e[0]))+r.x,(-1!==e[1].indexOf("%")?parseFloat(e[1])/100*r.height:parseFloat(e[1]))+r.y]),i.xOrigin=parseFloat(n[0]),i.yOrigin=parseFloat(n[1]),t.setAttribute("data-svg-origin",n.join(" "))},Me=U.getTransform=function(t,e,i,s){if(t._gsTransform&&i&&!s)return t._gsTransform;var n,o,h,l,_,u,p,f,c,m,d=i?t._gsTransform||new ke:new ke,g=0>d.scaleX,v=2e-5,y=1e5,T=Se?parseFloat(Q(t,Pe,e,!1,"0 0 0").split(" ")[2])||d.zOrigin||0:0,w=parseFloat(a.defaultTransformPerspective)||0;if(xe?o=Q(t,be,e,!0):t.currentStyle&&(o=t.currentStyle.filter.match(C),o=o&&4===o.length?[o[0].substr(4),Number(o[2].substr(4)),Number(o[1].substr(4)),o[3].substr(4),d.x||0,d.y||0].join(","):""),n=!o||"none"===o||"matrix(1, 0, 0, 1, 0, 0)"===o,d.svg=!!(Re&&"function"==typeof t.getBBox&&t.getCTM&&(!t.parentNode||t.parentNode.getBBox&&t.parentNode.getCTM)),d.svg&&(n&&-1!==(t.style[xe]+"").indexOf("matrix")&&(o=t.style[xe],n=!1),De(t,Q(t,Pe,r,!1,"50% 50%")+"",d,t.getAttribute("data-svg-origin")),Te=a.useSVGTransformAttr||Ce,h=t.getAttribute("transform"),n&&h&&-1!==h.indexOf("matrix")&&(o=h,n=0)),!n){for(h=(o||"").match(/(?:\-|\b)[\d\-\.e]+\b/gi)||[],l=h.length;--l>-1;)_=Number(h[l]),h[l]=(u=_-(_|=0))?(0|u*y+(0>u?-.5:.5))/y+_:_;if(16===h.length){var x,b,P,S,k,R=h[0],A=h[1],O=h[2],D=h[3],M=h[4],z=h[5],F=h[6],E=h[7],N=h[8],L=h[9],X=h[10],U=h[12],Y=h[13],j=h[14],B=h[11],q=Math.atan2(F,X);d.zOrigin&&(j=-d.zOrigin,U=N*j-h[12],Y=L*j-h[13],j=X*j+d.zOrigin-h[14]),d.rotationX=q*I,q&&(S=Math.cos(-q),k=Math.sin(-q),x=M*S+N*k,b=z*S+L*k,P=F*S+X*k,N=M*-k+N*S,L=z*-k+L*S,X=F*-k+X*S,B=E*-k+B*S,M=x,z=b,F=P),q=Math.atan2(N,X),d.rotationY=q*I,q&&(S=Math.cos(-q),k=Math.sin(-q),x=R*S-N*k,b=A*S-L*k,P=O*S-X*k,L=A*k+L*S,X=O*k+X*S,B=D*k+B*S,R=x,A=b,O=P),q=Math.atan2(A,R),d.rotation=q*I,q&&(S=Math.cos(-q),k=Math.sin(-q),R=R*S+M*k,b=A*S+z*k,z=A*-k+z*S,F=O*-k+F*S,A=b),d.rotationX&&Math.abs(d.rotationX)+Math.abs(d.rotation)>359.9&&(d.rotationX=d.rotation=0,d.rotationY+=180),d.scaleX=(0|Math.sqrt(R*R+A*A)*y+.5)/y,d.scaleY=(0|Math.sqrt(z*z+L*L)*y+.5)/y,d.scaleZ=(0|Math.sqrt(F*F+X*X)*y+.5)/y,d.skewX=0,d.perspective=B?1/(0>B?-B:B):0,d.x=U,d.y=Y,d.z=j,d.svg&&(d.x-=d.xOrigin-(d.xOrigin*R-d.yOrigin*M),d.y-=d.yOrigin-(d.yOrigin*A-d.xOrigin*z))}else if(!(Se&&!s&&h.length&&d.x===h[4]&&d.y===h[5]&&(d.rotationX||d.rotationY)||void 0!==d.x&&"none"===Q(t,"display",e))){var V=h.length>=6,G=V?h[0]:1,W=h[1]||0,Z=h[2]||0,$=V?h[3]:1;d.x=h[4]||0,d.y=h[5]||0,p=Math.sqrt(G*G+W*W),f=Math.sqrt($*$+Z*Z),c=G||W?Math.atan2(W,G)*I:d.rotation||0,m=Z||$?Math.atan2(Z,$)*I+c:d.skewX||0,Math.abs(m)>90&&270>Math.abs(m)&&(g?(p*=-1,m+=0>=c?180:-180,c+=0>=c?180:-180):(f*=-1,m+=0>=m?180:-180)),d.scaleX=p,d.scaleY=f,d.rotation=c,d.skewX=m,Se&&(d.rotationX=d.rotationY=d.z=0,d.perspective=w,d.scaleZ=1),d.svg&&(d.x-=d.xOrigin-(d.xOrigin*G-d.yOrigin*W),d.y-=d.yOrigin-(d.yOrigin*$-d.xOrigin*Z))}d.zOrigin=T;for(l in d)v>d[l]&&d[l]>-v&&(d[l]=0)}return i&&(t._gsTransform=d,d.svg&&(Te&&t.style[xe]?Ne(t.style,xe):!Te&&t.getAttribute("transform")&&t.removeAttribute("transform"))),d},ze=function(t){var e,i,s=this.data,r=-s.rotation*z,n=r+s.skewX*z,a=1e5,o=(0|Math.cos(r)*s.scaleX*a)/a,h=(0|Math.sin(r)*s.scaleX*a)/a,l=(0|Math.sin(n)*-s.scaleY*a)/a,_=(0|Math.cos(n)*s.scaleY*a)/a,u=this.t.style,p=this.t.currentStyle;if(p){i=h,h=-l,l=-i,e=p.filter,u.filter="";var f,c,d=this.t.offsetWidth,g=this.t.offsetHeight,v="absolute"!==p.position,y="progid:DXImageTransform.Microsoft.Matrix(M11="+o+", M12="+h+", M21="+l+", M22="+_,x=s.x+d*s.xPercent/100,b=s.y+g*s.yPercent/100;if(null!=s.ox&&(f=(s.oxp?.01*d*s.ox:s.ox)-d/2,c=(s.oyp?.01*g*s.oy:s.oy)-g/2,x+=f-(f*o+c*h),b+=c-(f*l+c*_)),v?(f=d/2,c=g/2,y+=", Dx="+(f-(f*o+c*h)+x)+", Dy="+(c-(f*l+c*_)+b)+")"):y+=", sizingMethod='auto expand')",u.filter=-1!==e.indexOf("DXImageTransform.Microsoft.Matrix(")?e.replace(D,y):y+" "+e,(0===t||1===t)&&1===o&&0===h&&0===l&&1===_&&(v&&-1===y.indexOf("Dx=0, Dy=0")||w.test(e)&&100!==parseFloat(RegExp.$1)||-1===e.indexOf("gradient("&&e.indexOf("Alpha"))&&u.removeAttribute("filter")),!v){var P,S,k,R=8>m?1:-1;for(f=s.ieOffsetX||0,c=s.ieOffsetY||0,s.ieOffsetX=Math.round((d-((0>o?-o:o)*d+(0>h?-h:h)*g))/2+x),s.ieOffsetY=Math.round((g-((0>_?-_:_)*g+(0>
1726l?-l:l)*d))/2+b),de=0;4>de;de++)S=ee[de],P=p[S],i=-1!==P.indexOf("px")?parseFloat(P):$(this.t,S,parseFloat(P),P.replace(T,""))||0,k=i!==s[S]?2>de?-s.ieOffsetX:-s.ieOffsetY:2>de?f-s.ieOffsetX:c-s.ieOffsetY,u[S]=(s[S]=Math.round(i-k*(0===de||2===de?1:R)))+"px"}}},Ie=U.set3DTransformRatio=function(t){var e,i,s,r,n,a,o,h,l,_,u,p,c,m,d,g,v,y,T,w,x,b=this.data,P=this.t.style,S=b.rotation*z,k=b.scaleX,R=b.scaleY,A=b.scaleZ,O=b.x,C=b.y,D=b.z,M=b.perspective;if(!(1!==t&&0!==t&&b.force3D||b.force3D===!0||b.rotationY||b.rotationX||1!==A||M||D||this.tween._totalTime!==this.tween._totalDuration&&this.tween._totalTime))return Fe.call(this,t),void 0;if(f&&(m=1e-4,m>k&&k>-m&&(k=A=2e-5),m>R&&R>-m&&(R=A=2e-5),!M||b.z||b.rotationX||b.rotationY||(M=0)),S||b.skewX)d=e=Math.cos(S),g=r=Math.sin(S),b.skewX&&(S-=b.skewX*z,d=Math.cos(S),g=Math.sin(S),"simple"===b.skewType&&(v=Math.tan(b.skewX*z),v=Math.sqrt(1+v*v),d*=v,g*=v)),i=-g,n=d;else{if(!(b.rotationY||b.rotationX||1!==A||M||b.svg))return P[xe]=(b.xPercent||b.yPercent?"translate("+b.xPercent+"%,"+b.yPercent+"%) translate3d(":"translate3d(")+O+"px,"+C+"px,"+D+"px)"+(1!==k||1!==R?" scale("+k+","+R+")":""),void 0;e=n=1,i=r=0}l=1,s=a=o=h=_=u=0,p=M?-1/M:0,c=b.zOrigin,m=1e-6,w=",",x="0",S=b.rotationY*z,S&&(d=Math.cos(S),g=Math.sin(S),o=-g,_=p*-g,s=e*g,a=r*g,l=d,p*=d,e*=d,r*=d),S=b.rotationX*z,S&&(d=Math.cos(S),g=Math.sin(S),v=i*d+s*g,y=n*d+a*g,h=l*g,u=p*g,s=i*-g+s*d,a=n*-g+a*d,l*=d,p*=d,i=v,n=y),1!==A&&(s*=A,a*=A,l*=A,p*=A),1!==R&&(i*=R,n*=R,h*=R,u*=R),1!==k&&(e*=k,r*=k,o*=k,_*=k),(c||b.svg)&&(c&&(O+=s*-c,C+=a*-c,D+=l*-c+c),b.svg&&(O+=b.xOrigin-(b.xOrigin*e+b.yOrigin*i),C+=b.yOrigin-(b.xOrigin*r+b.yOrigin*n)),m>O&&O>-m&&(O=x),m>C&&C>-m&&(C=x),m>D&&D>-m&&(D=0)),T=b.xPercent||b.yPercent?"translate("+b.xPercent+"%,"+b.yPercent+"%) matrix3d(":"matrix3d(",T+=(m>e&&e>-m?x:e)+w+(m>r&&r>-m?x:r)+w+(m>o&&o>-m?x:o),T+=w+(m>_&&_>-m?x:_)+w+(m>i&&i>-m?x:i)+w+(m>n&&n>-m?x:n),b.rotationX||b.rotationY?(T+=w+(m>h&&h>-m?x:h)+w+(m>u&&u>-m?x:u)+w+(m>s&&s>-m?x:s),T+=w+(m>a&&a>-m?x:a)+w+(m>l&&l>-m?x:l)+w+(m>p&&p>-m?x:p)+w):T+=",0,0,0,0,1,0,",T+=O+w+C+w+D+w+(M?1+-D/M:1)+")",P[xe]=T},Fe=U.set2DTransformRatio=function(t){var e,i,s,r,n,a,o,h,l,_,u,p,f=this.data,c=this.t,m=c.style,d=f.x,g=f.y;return!(f.rotationX||f.rotationY||f.z||f.force3D===!0||"auto"===f.force3D&&1!==t&&0!==t)||f.svg&&Te||!Se?(r=f.scaleX,n=f.scaleY,f.rotation||f.skewX||f.svg?(e=f.rotation*z,i=f.skewX*z,s=1e5,a=Math.cos(e)*r,o=Math.sin(e)*r,h=Math.sin(e-i)*-n,l=Math.cos(e-i)*n,i&&"simple"===f.skewType&&(p=Math.tan(i),p=Math.sqrt(1+p*p),h*=p,l*=p),f.svg&&(d+=f.xOrigin-(f.xOrigin*a+f.yOrigin*h),g+=f.yOrigin-(f.xOrigin*o+f.yOrigin*l),u=1e-6,u>d&&d>-u&&(d=0),u>g&&g>-u&&(g=0)),_=(0|a*s)/s+","+(0|o*s)/s+","+(0|h*s)/s+","+(0|l*s)/s+","+d+","+g+")",f.svg&&Te?c.setAttribute("transform","matrix("+_):m[xe]=(f.xPercent||f.yPercent?"translate("+f.xPercent+"%,"+f.yPercent+"%) matrix(":"matrix(")+_):m[xe]=(f.xPercent||f.yPercent?"translate("+f.xPercent+"%,"+f.yPercent+"%) matrix(":"matrix(")+r+",0,0,"+n+","+d+","+g+")",void 0):(this.setRatio=Ie,Ie.call(this,t),void 0)};l=ke.prototype,l.x=l.y=l.z=l.skewX=l.skewY=l.rotation=l.rotationX=l.rotationY=l.zOrigin=l.xPercent=l.yPercent=0,l.scaleX=l.scaleY=l.scaleZ=1,ve("transform,scale,scaleX,scaleY,scaleZ,x,y,z,rotation,rotationX,rotationY,rotationZ,skewX,skewY,shortRotation,shortRotationX,shortRotationY,shortRotationZ,transformOrigin,svgOrigin,transformPerspective,directionalRotation,parseTransform,force3D,skewType,xPercent,yPercent",{parser:function(t,e,i,s,n,o,h){if(s._lastParsedTransform===h)return n;s._lastParsedTransform=h;var l,_,u,p,f,c,m,d=s._transform=Me(t,r,!0,h.parseTransform),g=t.style,v=1e-6,y=we.length,T=h,w={};if("string"==typeof T.transform&&xe)u=L.style,u[xe]=T.transform,u.display="block",u.position="absolute",E.body.appendChild(L),l=Me(L,null,!1),E.body.removeChild(L);else if("object"==typeof T){if(l={scaleX:ne(null!=T.scaleX?T.scaleX:T.scale,d.scaleX),scaleY:ne(null!=T.scaleY?T.scaleY:T.scale,d.scaleY),scaleZ:ne(T.scaleZ,d.scaleZ),x:ne(T.x,d.x),y:ne(T.y,d.y),z:ne(T.z,d.z),xPercent:ne(T.xPercent,d.xPercent),yPercent:ne(T.yPercent,d.yPercent),perspective:ne(T.transformPerspective,d.perspective)},m=T.directionalRotation,null!=m)if("object"==typeof m)for(u in m)T[u]=m[u];else T.rotation=m;"string"==typeof T.x&&-1!==T.x.indexOf("%")&&(l.x=0,l.xPercent=ne(T.x,d.xPercent)),"string"==typeof T.y&&-1!==T.y.indexOf("%")&&(l.y=0,l.yPercent=ne(T.y,d.yPercent)),l.rotation=ae("rotation"in T?T.rotation:"shortRotation"in T?T.shortRotation+"_short":"rotationZ"in T?T.rotationZ:d.rotation,d.rotation,"rotation",w),Se&&(l.rotationX=ae("rotationX"in T?T.rotationX:"shortRotationX"in T?T.shortRotationX+"_short":d.rotationX||0,d.rotationX,"rotationX",w),l.rotationY=ae("rotationY"
1726in T?T.rotationY:"shortRotationY"in T?T.shortRotationY+"_short":d.rotationY||0,d.rotationY,"rotationY",w)),l.skewX=null==T.skewX?d.skewX:ae(T.skewX,d.skewX),l.skewY=null==T.skewY?d.skewY:ae(T.skewY,d.skewY),(_=l.skewY-d.skewY)&&(l.skewX+=_,l.rotation+=_)}for(Se&&null!=T.force3D&&(d.force3D=T.force3D,c=!0),d.skewType=T.skewType||d.skewType||a.defaultSkewType,f=d.force3D||d.z||d.rotationX||d.rotationY||l.z||l.rotationX||l.rotationY||l.perspective,f||null==T.scale||(l.scaleZ=1);--y>-1;)i=we[y],p=l[i]-d[i],(p>v||-v>p||null!=T[i]||null!=F[i])&&(c=!0,n=new ce(d,i,d[i],p,n),i in w&&(n.e=w[i]),n.xs0=0,n.plugin=o,s._overwriteProps.push(n.n));return p=T.transformOrigin,d.svg&&(p||T.svgOrigin)&&(De(t,se(p),l,T.svgOrigin),n=new ce(d,"xOrigin",d.xOrigin,l.xOrigin-d.xOrigin,n,-1,"transformOrigin"),n.b=d.xOrigin,n.e=n.xs0=l.xOrigin,n=new ce(d,"yOrigin",d.yOrigin,l.yOrigin-d.yOrigin,n,-1,"transformOrigin"),n.b=d.yOrigin,n.e=n.xs0=l.yOrigin,p=Te?null:"0px 0px"),(p||Se&&f&&d.zOrigin)&&(xe?(c=!0,i=Pe,p=(p||Q(t,i,r,!1,"50% 50%"))+"",n=new ce(g,i,0,0,n,-1,"transformOrigin"),n.b=g[i],n.plugin=o,Se?(u=d.zOrigin,p=p.split(" "),d.zOrigin=(p.length>2&&(0===u||"0px"!==p[2])?parseFloat(p[2]):u)||0,n.xs0=n.e=p[0]+" "+(p[1]||"50%")+" 0px",n=new ce(d,"zOrigin",0,0,n,-1,n.n),n.b=u,n.xs0=n.e=d.zOrigin):n.xs0=n.e=p):se(p+"",d)),c&&(s._transformType=d.svg&&Te||!f&&3!==this._transformType?2:3),n},prefix:!0}),ve("boxShadow",{defaultValue:"0px 0px 0px 0px #999",prefix:!0,color:!0,multi:!0,keyword:"inset"}),ve("borderRadius",{defaultValue:"0px",parser:function(t,e,i,n,a){e=this.format(e);var o,h,l,_,u,p,f,c,m,d,g,v,y,T,w,x,b=["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],P=t.style;for(m=parseFloat(t.offsetWidth),d=parseFloat(t.offsetHeight),o=e.split(" "),h=0;b.length>h;h++)this.p.indexOf("border")&&(b[h]=W(b[h])),u=_=Q(t,b[h],r,!1,"0px"),-1!==u.indexOf(" ")&&(_=u.split(" "),u=_[0],_=_[1]),p=l=o[h],f=parseFloat(u),v=u.substr((f+"").length),y="="===p.charAt(1),y?(c=parseInt(p.charAt(0)+"1",10),p=
vendor: 9,852 bytes, lines 1726-1727
1726p.substr(2),c*=parseFloat(p),g=p.substr((c+"").length-(0>c?1:0))||""):(c=parseFloat(p),g=p.substr((c+"").length)),""===g&&(g=s[i]||v),g!==v&&(T=$(t,"borderLeft",f,v),w=$(t,"borderTop",f,v),"%"===g?(u=100*(T/m)+"%",_=100*(w/d)+"%"):"em"===g?(x=$(t,"borderLeft",1,"em"),u=T/x+"em",_=w/x+"em"):(u=T+"px",_=w+"px"),y&&(p=parseFloat(u)+c+g,l=parseFloat(_)+c+g)),a=me(P,b[h],u+" "+_,p+" "+l,!1,"0px",a);return a},prefix:!0,formatter:ue("0px 0px 0px 0px",!1,!0)}),ve("backgroundPosition",{defaultValue:"0 0",parser:function(t,e,i,s,n,a){var o,h,l,_,u,p,f="background-position",c=r||Z(t,null),d=this.format((c?m?c.getPropertyValue(f+"-x")+" "+c.getPropertyValue(f+"-y"):c.getPropertyValue(f):t.currentStyle.backgroundPositionX+" "+t.currentStyle.backgroundPositionY)||"0 0"),g=this.format(e);if(-1!==d.indexOf("%")!=(-1!==g.indexOf("%"))&&(p=Q(t,"backgroundImage").replace(R,""),p&&"none"!==p)){for(o=d.split(" "),h=g.split(" "),X.setAttribute("src",p),l=2;--l>-1;)d=o[l],_=-1!==d.indexOf("%"),_!==(-1!==h[l].indexOf("%"))&&(u=0===l?t.offsetWidth-X.width:t.offsetHeight-X.height,o[l]=_?parseFloat(d)/100*u+"px":100*(parseFloat(d)/u)+"%");d=o.join(" ")}return this.parseComplex(t.style,d,g,n,a)},formatter:se}),ve("backgroundSize",{defaultValue:"0 0",formatter:se}),ve("perspective",{defaultValue:"0px",prefix:!0}),ve("perspectiveOrigin",{defaultValue:"50% 50%",prefix:!0}),ve("transformStyle",{prefix:!0}),ve("backfaceVisibility",{prefix:!0}),ve("userSelect",{prefix:!0}),ve("margin",{parser:pe("marginTop,marginRight,marginBottom,marginLeft")}),ve("padding",{parser:pe("paddingTop,paddingRight,paddingBottom,paddingLeft")}),ve("clip",{defaultValue:"rect(0px,0px,0px,0px)",parser:function(t,e,i,s,n,a){var o,h,l;return 9>m?(h=t.currentStyle,l=8>m?" ":",",o="rect("+h.clipTop+l+h.clipRight+l+h.clipBottom+l+h.clipLeft+")",e=this.format(e).split(",").join(l)):(o=this.format(Q(t,this.p,r,!1,this.dflt)),e=this.format(e)),this.parseComplex(t.style,o,e,n,a)}}),ve("textShadow",{defaultValue:"0px 0px 0px #999",color:!0,multi:!0}),ve("autoRound,strictUnits",{parser:function(t,e,i,s,r){return r}}),ve("border",{defaultValue:"0px solid #000",parser:function(t,e,i,s,n,a){return this.parseComplex(t.style,this.format(Q(t,"borderTopWidth",r,!1,"0px")+" "+Q(t,"borderTopStyle",r,!1,"solid")+" "+Q(t,"borderTopColor",r,!1,"#000")),this.format(e),n,a)},color:!0,formatter:function(t){var e=t.split(" ");return e[0]+" "+(e[1]||"solid")+" "+(t.match(_e)||["#000"])[0]}}),ve("borderWidth",{parser:pe("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}),ve("float,cssFloat,styleFloat",{parser:function(t,e,i,s,r){var n=t.style,a="cssFloat"in n?"cssFloat":"styleFloat";return new ce(n,a,0,0,r,-1,i,!1,0,n[a],e)}});var Ee=function(t){var e,i=this.t,s=i.filter||Q(this.data,"filter")||"",r=0|this.s+this.c*t;100===r&&(-1===s.indexOf("atrix(")&&-1===s.indexOf("radient(")&&-1===s.indexOf("oader(")?(i.removeAttribute("filter"),e=!Q(this.data,"filter")):(i.filter=s.replace(b,""),e=!0)),e||(this.xn1&&(i.filter=s=s||"alpha(opacity="+r+")"),-1===s.indexOf("pacity")?0===r&&this.xn1||(i.filter=s+" alpha(opacity="+r+")"):i.filter=s.replace(w,"opacity="+r))};ve("opacity,alpha,autoAlpha",{defaultValue:"1",parser:function(t,e,i,s,n,a){var o=parseFloat(Q(t,"opacity",r,!1,"1")),h=t.style,l="autoAlpha"===i;return"string"==typeof e&&"="===e.charAt(1)&&(e=("-"===e.charAt(0)?-1:1)*parseFloat(e.substr(2))+o),l&&1===o&&"hidden"===Q(t,"visibility",r)&&0!==e&&(o=0),j?n=new ce(h,"opacity",o,e-o,n):(n=new ce(h,"opacity",100*o,100*(e-o),n),n.xn1=l?1:0,h.zoom=1,n.type=2,n.b="alpha(opacity="+n.s+")",n.e="alpha(opacity="+(n.s+n.c)+")",n.data=t,n.plugin=a,n.setRatio=Ee),l&&(n=new ce(h,"visibility",0,0,n,-1,null,!1,0,0!==o?"inherit":"hidden",0===e?"hidden":"inherit"),n.xs0="inherit",s._overwriteProps.push(n.n),s._overwriteProps.push(i)),n}});var Ne=function(t,e){e&&(t.removeProperty?(("ms"===e.substr(0,2)||"webkit"===e.substr(0,6))&&(e="-"+e),t.removeProperty(e.replace(S,"-$1").toLowerCase())):t.removeAttribute(e)) 1727},Le=function(t){if(this.t._gsClassPT=this,1===t||0===t){this.t.setAttribute("class",0===t?this.b:this.e);for(var e=this.data,i=this.t.style;e;)e.v?i[e.p]=e.v:Ne(i,e.p),e=e._next;1===t&&this.t._gsClassPT===this&&(this.t._gsClassPT=null)}else this.t.getAttribute("class")!==this.e&&this.t.setAttribute("class",this.e)};ve("className",{parser:function(t,e,s,n,a,o,h){var l,_,u,p,f,c=t.getAttribute("class")||"",m=t.style.cssText;if(a=n._classNamePT=new ce(t,s,0,0,a,2),a.setRatio=Le,a.pr=-11,i=!0,a.b=c,_=K(t,r),u=t._gsClassPT){for(p={},f=u.data;f;)p[f.p]=1,f=f._next;u.setRatio(1)}return t._gsClassPT=a,a.e="="!==e.charAt(1)?e:c.replace(RegExp("\\s*\\b"+e.substr(2)+"\\b"),"")+("+"===e.charAt(0)?" "+e.substr(2):""),n._tween._duration&&(t.setAttribute("class",a.e),l=J(t,_,K(t),h,p),t.setAttribute("class",c),a.data=l.firstMPT,t.style.cssText=m,a=a.xfirst=n.parse(t,l.difs,a,o)),a}});var Xe=function(t){if((1===t||0===t)&&this.data._totalTime===this.data._totalDuration&&"isFromStart"!==this.data.data){var e,i,s,r,n=this.t.style,a=h.transform.parse;if("all"===this.e)n.cssText="",r=!0;else for(e=this.e.split(" ").join("").split(","),s=e.length;--s>-1;)i=e[s],h[i]&&(h[i].parse===a?r=!0:i="transformOrigin"===i?Pe:h[i].p),Ne(n,i);r&&(Ne(n,xe),this.t._gsTransform&&delete this.t._gsTransform)}};for(ve("clearProps",{parser:function(t,e,s,r,n){return n=new ce(t,s,0,0,n,2),n.setRatio=Xe,n.e=e,n.pr=-10,n.data=r._tween,i=!0,n}}),l="bezier,throwProps,physicsProps,physics2D".split(","),de=l.length;de--;)ye(l[de]);l=a.prototype,l._firstPT=l._lastParsedTransform=l._transform=null,l._onInitTween=function(t,e,o){if(!t.nodeType)return!1;this._target=t,this._tween=o,this._vars=e,_=e.autoRound,i=!1,s=e.suffixMap||a.suffixMap,r=Z(t,""),n=this._overwriteProps;var h,l,f,m,d,g,v,y,T,w=t.style;if(u&&""===w.zIndex&&(h=Q(t,"zIndex",r),("auto"===h||""===h)&&this._addLazySet(w,"zIndex",0)),"string"==typeof e&&(m=w.cssText,h=K(t,r),w.cssText=m+";"+e,h=J(t,h,K(t)).difs,!j&&x.test(e)&&(h.opacity=parseFloat(RegExp.$1)),e=h,w.cssText=m),this._firstPT=l=this.parse(t,e,null),this._transformType){for(T=3===this._transformType,xe?p&&(u=!0,""===w.zIndex&&(v=Q(t,"zIndex",r),("auto"===v||""===v)&&this._addLazySet(w,"zIndex",0)),c&&this._addLazySet(w,"WebkitBackfaceVisibility",this._vars.WebkitBackfaceVisibility||(T?"visible":"hidden"))):w.zoom=1,f=l;f&&f._next;)f=f._next;y=new ce(t,"transform",0,0,null,2),this._linkCSSP(y,null,f),y.setRatio=T&&Se?Ie:xe?Fe:ze,y.data=this._transform||Me(t,r,!0),y.tween=o,n.pop()}if(i){for(;l;){for(g=l._next,f=m;f&&f.pr>l.pr;)f=f._next;(l._prev=f?f._prev:d)?l._prev._next=l:m=l,(l._next=f)?f._prev=l:d=l,l=g}this._firstPT=m}return!0},l.parse=function(t,e,i,n){var a,o,l,u,p,f,c,m,d,g,v=t.style;for(a in e)f=e[a],o=h[a],o?i=o.parse(t,f,a,this,i,n,e):(p=Q(t,a,r)+"",d="string"==typeof f,"color"===a||"fill"===a||"stroke"===a||-1!==a.indexOf("Color")||d&&P.test(f)?(d||(f=le(f),f=(f.length>3?"rgba(":"rgb(")+f.join(",")+")"),i=me(v,a,p,f,!0,"transparent",i,0,n)):!d||-1===f.indexOf(" ")&&-1===f.indexOf(",")?(l=parseFloat(p),c=l||0===l?p.substr((l+"").length):"",(""===p||"auto"===p)&&("width"===a||"height"===a?(l=ie(t,a,r),c="px"):"left"===a||"top"===a?(l=H(t,a,r),c="px"):(l="opacity"!==a?0:1,c="")),g=d&&"="===f.charAt(1),g?(u=parseInt(f.charAt(0)+"1",10),f=f.substr(2),u*=parseFloat(f),m=f.replace(T,"")):(u=parseFloat(f),m=d?f.replace(T,""):""),""===m&&(m=a in s?s[a]:c),f=u||0===u?(g?u+l:u)+m:e[a],c!==m&&""!==m&&(u||0===u)&&l&&(l=$(t,a,l,c),"%"===m?(l/=$(t,a,100,"%")/100,e.strictUnits!==!0&&(p=l+"%")):"em"===m?l/=$(t,a,1,"em"):"px"!==m&&(u=$(t,a,u,m),m="px"),g&&(u||0===u)&&(f=u+l+m)),g&&(u+=l),!l&&0!==l||!u&&0!==u?void 0!==v[a]&&(f||"NaN"!=f+""&&null!=f)?(i=new ce(v,a,u||l||0,0,i,-1,a,!1,0,p,f),i.xs0="none"!==f||"display"!==a&&-1===a.indexOf("Style")?f:p):q("invalid "+a+" tween value: "+e[a]):(i=new ce(v,a,l,u-l,i,0,a,_!==!1&&("px"===m||"zIndex"===a),0,p,f),i.xs0=m)):i=me(v,a,p,f,!0,null,i,0,n)),n&&i&&!i.plugin&&(i.plugin=n);return i},l.setRatio=function(t){var e,i,s,r=this._firstPT,n=1e-6;if(1!==t||this._tween._time!==this._tween._duration&&0!==this._tween._time)if(t||this._tween._time!==this._tween._duration&&0!==this._tween._time||this._tween._rawPrevTime===-1e-6)for(;r;){if(e=r.c*t+r.s,r.r?e=Math.round(e):n>e&&e>-n&&(e=0),r.type)if(1===r.type)if(s=r.l,2===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2;else if(3===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3;else if(4===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3+r.xn3+r.xs4;else if(5===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3+r.xn3+r.xs4+r.xn4+r.xs5;else{for(i=r.xs0+e+r.xs1,s=1;r.l>s;s++)i+=r["xn"+s]+r["xs"+(s+1)];r.t[r.p]=i}else-1===r.type?r.t[r.p]=r.xs0:r.setRatio&&r.setRatio(t);else r.t[r.p]=e+r.xs0;r=r._next}else for(;r;)2!==r.type?r.t[r.p]=r.b:r.setRatio(t),r=r._next;else for(;r;)2!==r.type?r.t[r.p]=r.e:r.setRatio(t),r=r._next},l._enableTransforms=function(t){this._transform=this._transform||Me(this._target,r,!0),this._transformType=this._transform.svg&&Te||!t&&3!==this._transformType?2:3};var Ue=function(){this.t[this.p]=this.e,this.data._linkCSSP(this,this._next,null,!0)};l._addLazySet=function(t,e,i){var s=this._firstPT=new ce(t,e,0,0,this._firstPT,2);s.e=i,s.setRatio=Ue,s.data=this},l._linkCSSP=function(t,e,i,s){return t&&(e&&(e._prev=t),t._next&&(t._next._prev=t._prev),t._prev?t._prev._next=t._next:this._firstPT===t&&(this._firstPT=t._next,s=!0),i?i._next=t:s||null!==this._firstPT||(this._firstPT=t),t._next=e,t._prev=i),t},l._kill=function(e){var i,s,r,n=e;if(e.autoAlpha||e.alpha){n={};for(s in e)n[s]=e[s];n.opacity=1,n.autoAlpha&&(n.visibility=1)}return e.className&&(i=this._classNamePT)&&(r=i.xfirst,r&&r._prev?this._linkCSSP(r._prev,i._next,r._prev._prev):r===this._firstPT&&(this._firstPT=i._next),i._next&&this._linkCSSP(i._next,i._next._next,r._prev),this._classNamePT=null),t.prototype._kill.call(this,n)};
1727var Ye=function(t,e,i){var s,r,n,a;if(t.slice)for(r=t.length;--r>-1;)Ye(t[r],e,i);else for(s=t.childNodes,r=s.length;--r>-1;)n=s[r],a=n.type,n.style&&(e.push(K(n)),i&&i.push(n)),1!==a&&9!==a&&11!==a||!n.childNodes.length||Ye(n,e,i)};return a.cascadeTo=function(t,i,s){var r,n,a,o,h=e.to(t,i,s),l=[h],_=[],u=[],p=[],f=e._internals.reservedProps;for(t=h._targets||h.target,Ye(t,_,p),h.render(i,!0,!0),Ye(t,u),h.render(0,!0,!0),h._enabled(!0),r=p.length;--r>-1;)if(n=J(p[r],_[r],u[r]),n.firstMPT){n=n.difs;for(a in s)f[a]&&(n[a]=s[a]);o={};for(a in n)o[a]=_[r][a];l.push(e.fromTo(p[r],i,o,n))}return l},t.activate([a]),a},!0),function(){var t=_gsScope._gsDefine.plugin({propName:"roundProps",priority:-1,API:2,init:function(t,e,i){return this._tween=i,!0}}),e=t.prototype;e._onInitAllProps=function(){for(var t,e,i,s=this._tween,r=s.vars.roundProps instanceof Array?s.vars.roundProps:s.vars.roundProps.split(","),n=r.length,a={},o=s._propLookup.roundProps;--n>-1;)a[r[n]]=1;for(n=r.length;--n>-1;)for(t=r[n],e=s._firstPT;e;)i=e._next,e.pg?e.t._roundProps(a,!0):e.n===t&&(this._add(e.t,t,e.s,e.c),i&&(i._prev=e._prev),e._prev?e._prev._next=i:s._firstPT===e&&(s._firstPT=i),e._next=e._prev=null,s._propLookup[t]=o),e=i;return!1},e._add=function(t,e,i,s){this._addTween(t,e,i,i+s,e,!0),this._overwriteProps.push(e)}}(),_gsScope._gsDefine.plugin({propName:"attr",API:2,version:"0.3.3",init:function(t,e){var i,s,r;if("function"!=typeof t.setAttribute)return!1;this._target=t,this._proxy={},this._start={},this._end={};for(i in e)this._start[i]=this._proxy[i]=s=t.getAttribute(i),r=this._addTween(this._proxy,i,parseFloat(s),e[i],i),this._end[i]=r?r.s+r.c:e[i],this._overwriteProps.push(i);return!0},set:function(t){this._super.setRatio.call(this,t);for(var e,i=this._overwriteProps,s=i.length,r=1===t?this._end:t?this._proxy:this._start;--s>-1;)e=i[s],this._target.setAttribute(e,r[e]+"")}}),_gsScope._gsDefine.plugin({propName:"directionalRotation",version:"0.2.1",API:2,init:function(t,e){"object"!=typeof e&&(e={rotation:e}),this.finals={};var i,s,r,n,a,o,h=e.useRadians===!0?2*Math.PI:360,l=1e-6;for(i in e)"useRadians"!==i&&(o=(e[i]+"").split("_"),s=o[0],r=parseFloat("function"!=typeof t[i]?t[i]:t[i.indexOf("set")||"function"!=typeof t["get"+i.substr(3)]?i:"get"+i.substr(3)]()),n=this.finals[i]="string"==typeof s&&"="===s.charAt(1)?r+parseInt(s.charAt(0)+"1",10)*Number(s.substr(2)):Number(s)||0,a=n-r,o.length&&(s=o.join("_"),-1!==s.indexOf("short")&&(a%=h,a!==a%(h/2)&&(a=0>a?a+h:a-h)),-1!==s.indexOf("_cw")&&0>a?a=(a+9999999999*h)%h-(0|a/h)*h:-1!==s.indexOf("ccw")&&a>0&&(a=(a-9999999999*h)%h-(0|a/h)*h)),(a>l||-l>a)&&(this._addTween(t,i,r,r+a,i),this._overwriteProps.push(i)));return!0},set:function(t){var e;if(1!==t)this._super.setRatio.call(this,t);else for(e=this._firstPT;e;)e.f?e.t[e.p](this.finals[e.p]):e.t[e.p]=this.finals[e.p],e=e._next}})._autoCSS=!0,_gsScope._gsDefine("easing.Back",["easing.Ease"],function(t){var e,i,s,r=_gsScope.GreenSockGlobals||_gsScope,n=r.com.greensock,a=2*Math.PI,o=Math.PI/2,h=n._class,l=function(e,i){var s=h("easing."+e,function(){},!0),r=s.prototype=new t;return r.constructor=s,r.getRatio=i,s},_=t.register||function(){},u=function(t,e,i,s){var r=h("easing."+t,{easeOut:new e,easeIn:new i,easeInOut:new s},!0);return _(r,t),r},p=function(t,e,i){this.t=t,this.v=e,i&&(this.next=i,i.prev=this,this.c=i.v-e,this.gap=i.t-t)},f=function(e,i){var s=h("easing."+e,function(t){this._p1=t||0===t?t:1.70158,this._p2=1.525*this._p1},!0),r=s.prototype=new t;return r.constructor=s,r.getRatio=i,r.config=function(t){return new s(t)},s},c=u("Back",f("BackOut",function(t){return(t-=1)*t*((this._p1+1)*t+this._p1)+1}),f("BackIn",function(t){return t*t*((this._p1+1)*t-this._p1)}),f("BackInOut",function(t){return 1>(t*=2)?.5*t*t*((this._p2+1)*t-this._p2):.5*((t-=2)*t*((this._p2+1)*t+this._p2)+2)})),m=h("easing.SlowMo",function(t,e,i){e=e||0===e?e:.7,null==t?t=.7:t>1&&(t=1),this._p=1!==t?e:0,this._p1=(1-t)/2,this._p2=t,this._p3=this._p1+this._p2,this._calcEnd=i===!0},!0),d=m.prototype=new t;return d.constructor=m,d.getRatio=function(t){var e=t+(.5-t)*this._p;return this._p1>t?this._calcEnd?1-(t=1-t/this._p1)*t:e-(t=1-t/this._p1)*t*t*t*e:t>this._p3?this._calcEnd?1-(t=(t-this._p3)/this._p1)*t:e+(t-e)*(t=(t-this._p3)/this._p1)*t*t*t:this._calcEnd?1:e}
vendor: 9,306 bytes, line 1727
1727,m.ease=new m(.7,.7),d.config=m.config=function(t,e,i){return new m(t,e,i)},e=h("easing.SteppedEase",function(t){t=t||1,this._p1=1/t,this._p2=t+1},!0),d=e.prototype=new t,d.constructor=e,d.getRatio=function(t){return 0>t?t=0:t>=1&&(t=.999999999),(this._p2*t>>0)*this._p1},d.config=e.config=function(t){return new e(t)},i=h("easing.RoughEase",function(e){e=e||{};for(var i,s,r,n,a,o,h=e.taper||"none",l=[],_=0,u=0|(e.points||20),f=u,c=e.randomize!==!1,m=e.clamp===!0,d=e.template instanceof t?e.template:null,g="number"==typeof e.strength?.4*e.strength:.4;--f>-1;)i=c?Math.random():1/u*f,s=d?d.getRatio(i):i,"none"===h?r=g:"out"===h?(n=1-i,r=n*n*g):"in"===h?r=i*i*g:.5>i?(n=2*i,r=.5*n*n*g):(n=2*(1-i),r=.5*n*n*g),c?s+=Math.random()*r-.5*r:f%2?s+=.5*r:s-=.5*r,m&&(s>1?s=1:0>s&&(s=0)),l[_++]={x:i,y:s};for(l.sort(function(t,e){return t.x-e.x}),o=new p(1,1,null),f=u;--f>-1;)a=l[f],o=new p(a.x,a.y,o);this._prev=new p(0,0,0!==o.t?o:o.next)},!0),d=i.prototype=new t,d.constructor=i,d.getRatio=function(t){var e=this._prev;if(t>e.t){for(;e.next&&t>=e.t;)e=e.next;e=e.prev}else for(;e.prev&&e.t>=t;)e=e.prev;return this._prev=e,e.v+(t-e.t)/e.gap*e.c},d.config=function(t){return new i(t)},i.ease=new i,u("Bounce",l("BounceOut",function(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}),l("BounceIn",function(t){return 1/2.75>(t=1-t)?1-7.5625*t*t:2/2.75>t?1-(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1-(7.5625*(t-=2.25/2.75)*t+.9375):1-(7.5625*(t-=2.625/2.75)*t+.984375)}),l("BounceInOut",function(t){var e=.5>t;return t=e?1-2*t:2*t-1,t=1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375,e?.5*(1-t):.5*t+.5})),u("Circ",l("CircOut",function(t){return Math.sqrt(1-(t-=1)*t)}),l("CircIn",function(t){return-(Math.sqrt(1-t*t)-1)}),l("CircInOut",function(t){return 1>(t*=2)?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)})),s=function(e,i,s){var r=h("easing."+e,function(t,e){this._p1=t>=1?t:1,this._p2=(e||s)/(1>t?t:1),this._p3=this._p2/a*(Math.asin(1/this._p1)||0),this._p2=a/this._p2},!0),n=r.prototype=new t;return n.constructor=r,n.getRatio=i,n.config=function(t,e){return new r(t,e)},r},u("Elastic",s("ElasticOut",function(t){return this._p1*Math.pow(2,-10*t)*Math.sin((t-this._p3)*this._p2)+1},.3),s("ElasticIn",function(t){return-(this._p1*Math.pow(2,10*(t-=1))*Math.sin((t-this._p3)*this._p2))},.3),s("ElasticInOut",function(t){return 1>(t*=2)?-.5*this._p1*Math.pow(2,10*(t-=1))*Math.sin((t-this._p3)*this._p2):.5*this._p1*Math.pow(2,-10*(t-=1))*Math.sin((t-this._p3)*this._p2)+1},.45)),u("Expo",l("ExpoOut",function(t){return 1-Math.pow(2,-10*t)}),l("ExpoIn",function(t){return Math.pow(2,10*(t-1))-.001}),l("ExpoInOut",function(t){return 1>(t*=2)?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*(t-1)))})),u("Sine",l("SineOut",function(t){return Math.sin(t*o)}),l("SineIn",function(t){return-Math.cos(t*o)+1}),l("SineInOut",function(t){return-.5*(Math.cos(Math.PI*t)-1)})),h("easing.EaseLookup",{find:function(e){return t.map[e]}},!0),_(r.SlowMo,"SlowMo","ease,"),_(i,"RoughEase","ease,"),_(e,"SteppedEase","ease,"),c},!0)}),_gsScope._gsDefine&&_gsScope._gsQueue.pop()(),function(t,e){"use strict";var i=t.GreenSockGlobals=t.GreenSockGlobals||t;if(!i.TweenLite){var s,r,n,a,o,h=function(t){var e,s=t.split("."),r=i;for(e=0;s.length>e;e++)r[s[e]]=r=r[s[e]]||{};return r},l=h("com.greensock"),_=1e-10,u=function(t){var e,i=[],s=t.length;for(e=0;e!==s;i.push(t[e++]));return i},p=function(){},f=function(){var t=Object.prototype.toString,e=t.call([]);return function(i){return null!=i&&(i instanceof Array||"object"==typeof i&&!!i.push&&t.call(i)===e)}}(),c={},m=function(s,r,n,a){this.sc=c[s]?c[s].sc:[],c[s]=this,this.gsClass=null,this.func=n;var o=[];this.check=function(l){for(var _,u,p,f,d=r.length,g=d;--d>-1;)(_=c[r[d]]||new m(r[d],[])).gsClass?(o[d]=_.gsClass,g--):l&&_.sc.push(this);if(0===g&&n)for(u=("com.greensock."+s).split("."),p=u.pop(),f=h(u.join("."))[p]=this.gsClass=n.apply(n,o),a&&(i[p]=f,"function"==typeof define&&define.amd?define((t.GreenSockAMDPath?t.GreenSockAMDPath+"/":"")+s.split(".").pop(),[],function(){return f}):s===e&&"undefined"!=typeof module&&module.exports&&(module.exports=f)),d=0;this.sc.length>d;d++)this.sc[d].check()},this.check(!0)},d=t._gsDefine=function(t,e,i,s){return new m(t,e,i,s)},g=l._class=function(t,e,i){return e=e||function(){},d(t,[],function(){return e},i),e};d.globals=i;var v=[0,0,1,1],y=[],T=g("easing.Ease",function(t,e,i,s){this._func=t,this._type=i||0,this._power=s||0,this._params=e?v.concat(e):v},!0),w=T.map={},x=T.register=function(t,e,i,s){for(var r,n,a,o,h=e.split(","),_=h.length,u=(i||"easeIn,easeOut,easeInOut").split(",");--_>-1;)for(n=h[_],r=s?g("easing."+n,null,!0):l.easing[n]||{},a=u.length;--a>-1;)o=u[a],w[n+"."+o]=w[o+n]=r[o]=t.getRatio?t:t[o]||new t};for(n=T.prototype,n._calcEnd=!1,n.getRatio=function(t){if(this._func)return this._params[0]=t,this._func.apply(null,this._params);var e=this._type,i=this._power,s=1===e?1-t:2===e?t:.5>t?2*t:2*(1-t);return 1===i?s*=s:2===i?s*=s*s:3===i?s*=s*s*s:4===i&&(s*=s*s*s*s),1===e?1-s:2===e?s:.5>t?s/2:1-s/2},s=["Linear","Quad","Cubic","Quart","Quint,Strong"],r=s.length;--r>-1;)n=s[r]+",Power"+r,x(new T(null,null,1,r),n,"easeOut",!0),x(new T(null,null,2,r),n,"easeIn"+(0===r?",easeNone":"")),x(new T(null,null,3,r),n,"easeInOut");w.linear=l.easing.Linear.easeIn,w.swing=l.easing.Quad.easeInOut;var b=g("events.EventDispatcher",function(t){this._listeners={},this._eventTarget=t||this});n=b.prototype,n.addEventListener=function(t,e,i,s,r){r=r||0;var n,h,l=this._listeners[t],_=0;for(null==l&&(this._listeners[t]=l=[]),h=l.length;--h>-1;)n=l[h],n.c===e&&n.s===i?l.splice(h,1):0===_&&r>n.pr&&(_=h+1);l.splice(_,0,{c:e,s:i,up:s,pr:r}),this!==a||o||a.wake()},n.removeEventListener=function(t,e){var i,s=this._listeners[t];if(s)for(i=s.length;--i>-1;)if(s[i].c===e)return s.splice(i,1),void 0},n.dispatchEvent=function(t){var e,i,s,r=this._listeners[t];if(r)for(e=r.length,i=this._eventTarget;--e>-1;)s=r[e],s&&(s.up?s.c.call(s.s||i,{type:t,target:i}):s.c.call(s.s||i))};var P=t.requestAnimationFrame,S=t.cancelAnimationFrame,k=Date.now||function(){return(new Date).getTime()},R=k();for(s=["ms","moz","webkit","o"],r=s.length;--r>-1&&!P;)P=t[s[r]+"RequestAnimationFrame"],S=t[s[r]+"CancelAnimationFrame"]||t[s[r]+"CancelRequestAnimationFrame"];g("Ticker",function(t,e){var i,s,r,n,h,l=this,u=k(),f=e!==!1&&P,c=500,m=33,d="tick",g=function(t){var e,a,o=k()-R;o>c&&(u+=o-m),R+=o,l.time=(R-u)/1e3,e=l.time-h,(!i||e>0||t===!0)&&(l.frame++,h+=e+(e>=n?.004:n-e),a=!0),t!==!0&&(r=s(g)),a&&l.dispatchEvent(d)};b.call(l),l.time=l.frame=0,l.tick=function(){g(!0)},l.lagSmoothing=function(t,e){c=t||1/_,m=Math.min(e,c,0)},l.sleep=function(){null!=r&&(f&&S?S(r):clearTimeout(r),s=p,r=null,l===a&&(o=!1))},l.wake=function(){null!==r?l.sleep():l.frame>10&&(R=k()-c+5),s=0===i?p:f&&P?P:function(t){return setTimeout(t,0|1e3*(h-l.time)+1)},l===a&&(o=!0),g(2)},l.fps=function(t){return arguments.length?(i=t,n=1/(i||60),h=this.time+n,l.wake(),void 0):i},l.useRAF=function(t){return arguments.length?(l.sleep(),f=t,l.fps(i),void 0):f},l.fps(t),setTimeout(function(){f&&(!r||5>l.frame)&&l.useRAF(!1)},1500)}),n=l.Ticker.prototype=new l.events.EventDispatcher,n.constructor=l.Ticker;var A=g("core.Animation",function(t,e){if(this.vars=e=e||{},this._duration=this._totalDuration=t||0,this._delay=Number(e.delay)||0,this._timeScale=1,this._active=e.immediateRender===!0,this.data=e.data,this._reversed=e.reversed===!0,B){o||a.wake();var i=this.vars.useFrames?j:B;i.add(this,i._time),this.vars.paused&&this.paused(!0)}});a=A.ticker=new l.Ticker,n=A.prototype,n._dirty=n._gc=n._initted=n._paused=!1,n._totalTime=n._time=0,n._rawPrevTime=-1,n._next=n._last=n._onUpdate=n._timeline=n.timeline=null,n._paused=!1;var O=function(){o&&k()-R>2e3&&a.wake(),setTimeout(O,2e3)};O(),n.play=function(t,e){return null!=t&&this.seek(t,e),this.reversed(!1).paused(!1)},n.pause=function(t,e){return null!=t&&this.seek(t,e),this.paused(!0)},n.resume=function(t,e){return null!=t&&this.seek(t,e),this.paused(!1)},n.seek=function(t,e){return this.totalTime(Number(t),e!==!1)},n.restart=function(t,e){return this.reversed(!1).paused(!1).totalTime(t?-this._delay:0,e!==!1,!0)},n.reverse=function(t,e){return null!=t&&this.seek(t||this.totalDuration(),e),this.reversed(!0).paused(!1)},n.render=function(){},n.invalidate=function(){return this._time=this._totalTime=0,this._initted=this._gc=!1,this._rawPrevTime=-1,(this._gc||!this.timeline)&&this._enabled(!0),this},n.isActive=function(){var t,e=this._timeline,i=this._startTime;return!e||!this._gc&&!this._paused&&e.isActive()&&(t=e.rawTime())>=i&&i+this.totalDuration()/this._timeScale>t},n._enabled=function(t,e){return o||a.wake(),this._gc=!t,this._active=this.isActive(),e!==!0&&(t&&!this.timeline?this._timeline.add(this,this._startTime-this._delay):!t&&this.timeline&&this._timeline._remove(this,!0)),!1},n._kill=function(){return this._enabled(!1,!1)},n.kill=function(t,e){return this._kill(t,e),this},n._uncache=function(t){for(var e=t?this:this.timeline;e;)e._dirty=!0,e=e.timeline;return this},n._swapSelfInParams=function(t){for(var e=t.length,i=t.concat();--e>-1;)"{self}"===t[e]&&(i[e]=this);return i}
vendor: 4,111 bytes, line 1727
1727,n.eventCallback=function(t,e,i,s){if("on"===(t||"").substr(0,2)){var r=this.vars;if(1===arguments.length)return r[t];null==e?delete r[t]:(r[t]=e,r[t+"Params"]=f(i)&&-1!==i.join("").indexOf("{self}")?this._swapSelfInParams(i):i,r[t+"Scope"]=s),"onUpdate"===t&&(this._onUpdate=e)}return this},n.delay=function(t){return arguments.length?(this._timeline.smoothChildTiming&&this.startTime(this._startTime+t-this._delay),this._delay=t,this):this._delay},n.duration=function(t){return arguments.length?(this._duration=this._totalDuration=t,this._uncache(!0),this._timeline.smoothChildTiming&&this._time>0&&this._time<this._duration&&0!==t&&this.totalTime(this._totalTime*(t/this._duration),!0),this):(this._dirty=!1,this._duration)},n.totalDuration=function(t){return this._dirty=!1,arguments.length?this.duration(t):this._totalDuration},n.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),this.totalTime(t>this._duration?this._duration:t,e)):this._time},n.totalTime=function(t,e,i){if(o||a.wake(),!arguments.length)return this._totalTime;if(this._timeline){if(0>t&&!i&&(t+=this.totalDuration()),this._timeline.smoothChildTiming){this._dirty&&this.totalDuration();var s=this._totalDuration,r=this._timeline;if(t>s&&!i&&(t=s),this._startTime=(this._paused?this._pauseTime:r._time)-(this._reversed?s-t:t)/this._timeScale,r._dirty||this._uncache(!1),r._timeline)for(;r._timeline;)r._timeline._time!==(r._startTime+r._totalTime)/r._timeScale&&r.totalTime(r._totalTime,!0),r=r._timeline}this._gc&&this._enabled(!0,!1),(this._totalTime!==t||0===this._duration)&&(this.render(t,e,!1),I.length&&V())}return this},n.progress=n.totalProgress=function(t,e){return arguments.length?this.totalTime(this.duration()*t,e):this._time/this.duration()},n.startTime=function(t){return arguments.length?(t!==this._startTime&&(this._startTime=t,this.timeline&&this.timeline._sortChildren&&this.timeline.add(this,t-this._delay)),this):this._startTime},n.endTime=function(t){return this._startTime+(0!=t?this.totalDuration():this.duration())/this._timeScale},n.timeScale=function(t){if(!arguments.length)return this._timeScale;if(t=t||_,this._timeline&&this._timeline.smoothChildTiming){var e=this._pauseTime,i=e||0===e?e:this._timeline.totalTime();this._startTime=i-(i-this._startTime)*this._timeScale/t}return this._timeScale=t,this._uncache(!1)},n.reversed=function(t){return arguments.length?(t!=this._reversed&&(this._reversed=t,this.totalTime(this._timeline&&!this._timeline.smoothChildTiming?this.totalDuration()-this._totalTime:this._totalTime,!0)),this):this._reversed},n.paused=function(t){if(!arguments.length)return this._paused;var e,i,s=this._timeline;return t!=this._paused&&s&&(o||t||a.wake(),e=s.rawTime(),i=e-this._pauseTime,!t&&s.smoothChildTiming&&(this._startTime+=i,this._uncache(!1)),this._pauseTime=t?e:null,this._paused=t,this._active=this.isActive(),!t&&0!==i&&this._initted&&this.duration()&&this.render(s.smoothChildTiming?this._totalTime:(e-this._startTime)/this._timeScale,!0,!0)),this._gc&&!t&&this._enabled(!0,!1),this};var C=g("core.SimpleTimeline",function(t){A.call(this,0,t),this.autoRemoveChildren=this.smoothChildTiming=!0});n=C.prototype=new A,n.constructor=C,n.kill()._gc=!1,n._first=n._last=n._recent=null,n._sortChildren=!1,n.add=n.insert=function(t,e){var i,s;if(t._startTime=Number(e||0)+t._delay,t._paused&&this!==t._timeline&&(t._pauseTime=t._startTime+(this.rawTime()-t._startTime)/t._timeScale),t.timeline&&t.timeline._remove(t,!0),t.timeline=t._timeline=this,t._gc&&t._enabled(!0,!0),i=this._last,this._sortChildren)for(s=t._startTime;i&&i._startTime>s;)i=i._prev;return i?(t._next=i._next,i._next=t):(t._next=this._first,this._first=t),t._next?t._next._prev=t:this._last=t,t._prev=i,this._recent=t,this._timeline&&this._uncache(!0),this},n._remove=function(t,e){return t.timeline===this&&(e||t._enabled(!1,!0),t._prev?t._prev._next=t._next:this._first===t&&(this._first=t._next),t._next?t._next._prev=t._prev:this._last===t&&(this._last=t._prev),t._next=t._prev=t.timeline=null,t===this._recent&&(this._recent=this._last),this._timeline&&this._uncache(!0)),this}
vendor: 12,956 bytes, lines 1727-1728
1727,n.render=function(t,e,i){var s,r=this._first;for(this._totalTime=this._time=this._rawPrevTime=t;r;)s=r._next,(r._active||t>=r._startTime&&!r._paused)&&(r._reversed?r.render((r._dirty?r.totalDuration():r._totalDuration)-(t-r._startTime)*r._timeScale,e,i):r.render((t-r._startTime)*r._timeScale,e,i)),r=s},n.rawTime=function(){return o||a.wake(),this._totalTime};var D=g("TweenLite",function(e,i,s){if(A.call(this,i,s),this.render=D.prototype.render,null==e)throw"Cannot tween a null target.";this.target=e="string"!=typeof e?e:D.selector(e)||e;var r,n,a,o=e.jquery||e.length&&e!==t&&e[0]&&(e[0]===t||e[0].nodeType&&e[0].style&&!e.nodeType),h=this.vars.overwrite;if(this._overwrite=h=null==h?Y[D.defaultOverwrite]:"number"==typeof h?h>>0:Y[h],(o||e instanceof Array||e.push&&f(e))&&"number"!=typeof e[0])for(this._targets=a=u(e),this._propLookup=[],this._siblings=[],r=0;a.length>r;r++)n=a[r],n?"string"!=typeof n?n.length&&n!==t&&n[0]&&(n[0]===t||n[0].nodeType&&n[0].style&&!n.nodeType)?(a.splice(r--,1),this._targets=a=a.concat(u(n))):(this._siblings[r]=G(n,this,!1),1===h&&this._siblings[r].length>1&&Z(n,this,null,1,this._siblings[r])):(n=a[r--]=D.selector(n),"string"==typeof n&&a.splice(r+1,1)):a.splice(r--,1);else this._propLookup={},this._siblings=G(e,this,!1),1===h&&this._siblings.length>1&&Z(e,this,null,1,this._siblings);(this.vars.immediateRender||0===i&&0===this._delay&&this.vars.immediateRender!==!1)&&(this._time=-_,this.render(-this._delay))},!0),M=function(e){return e&&e.length&&e!==t&&e[0]&&(e[0]===t||e[0].nodeType&&e[0].style&&!e.nodeType)},z=function(t,e){var i,s={};for(i in t)U[i]||i in e&&"transform"!==i&&"x"!==i&&"y"!==i&&"width"!==i&&"height"!==i&&"className"!==i&&"border"!==i||!(!N[i]||N[i]&&N[i]._autoCSS)||(s[i]=t[i],delete t[i]);t.css=s};n=D.prototype=new A,n.constructor=D,n.kill()._gc=!1,n.ratio=0,n._firstPT=n._targets=n._overwrittenProps=n._startAt=null,n._notifyPluginsOfEnabled=n._lazy=!1,D.version="1.16.0",D.defaultEase=n._ease=new T(null,null,1,1),D.defaultOverwrite="auto",D.ticker=a,D.autoSleep=120,D.lagSmoothing=function(t,e){a.lagSmoothing(t,e)},D.selector=t.$||t.jQuery||function(e){var i=t.$||t.jQuery;return i?(D.selector=i,i(e)):"undefined"==typeof document?e:document.querySelectorAll?document.querySelectorAll(e):document.getElementById("#"===e.charAt(0)?e.substr(1):e)};var I=[],F={},E=D._internals={isArray:f,isSelector:M,lazyTweens:I},N=D._plugins={},L=E.tweenLookup={},X=0,U=E.reservedProps={ease:1,delay:1,overwrite:1,onComplete:1,onCompleteParams:1,onCompleteScope:1,useFrames:1,runBackwards:1,startAt:1,onUpdate:1,onUpdateParams:1,onUpdateScope:1,onStart:1,onStartParams:1,onStartScope:1,onReverseComplete:1,onReverseCompleteParams:1,onReverseCompleteScope:1,onRepeat:1,onRepeatParams:1,onRepeatScope:1,easeParams:1,yoyo:1,immediateRender:1,repeat:1,repeatDelay:1,data:1,paused:1,reversed:1,autoCSS:1,lazy:1,onOverwrite:1},Y={none:0,all:1,auto:2,concurrent:3,allOnStart:4,preexisting:5,"true":1,"false":0},j=A._rootFramesTimeline=new C,B=A._rootTimeline=new C,q=30,V=E.lazyRender=function(){var t,e=I.length;for(F={};--e>-1;)t=I[e],t&&t._lazy!==!1&&(t.render(t._lazy[0],t._lazy[1],!0),t._lazy=!1);I.length=0};B._startTime=a.time,j._startTime=a.frame,B._active=j._active=!0,setTimeout(V,1),A._updateRoot=D.render=function(){var t,e,i;if(I.length&&V(),B.render((a.time-B._startTime)*B._timeScale,!1,!1),j.render((a.frame-j._startTime)*j._timeScale,!1,!1),I.length&&V(),a.frame>=q){q=a.frame+(parseInt(D.autoSleep,10)||120);for(i in L){for(e=L[i].tweens,t=e.length;--t>-1;)e[t]._gc&&e.splice(t,1);0===e.length&&delete L[i]}if(i=B._first,(!i||i._paused)&&D.autoSleep&&!j._first&&1===a._listeners.tick.length){for(;i&&i._paused;)i=i._next;i||a.sleep()}}},a.addEventListener("tick",A._updateRoot);var G=function(t,e,i){var s,r,n=t._gsTweenID;if(L[n||(t._gsTweenID=n="t"+X++)]||(L[n]={target:t,tweens:[]}),e&&(s=L[n].tweens,s[r=s.length]=e,i))for(;--r>-1;)s[r]===e&&s.splice(r,1);return L[n].tweens},W=function(t,e,i,s){var r,n,a=t.vars.onOverwrite;return a&&(r=a(t,e,i,s)),a=D.onOverwrite,a&&(n=a(t,e,i,s)),r!==!1&&n!==!1},Z=function(t,e,i,s,r){var n,a,o,h;if(1===s||s>=4){for(h=r.length,n=0;h>n;n++)if((o=r[n])!==e)o._gc||W(o,e)&&o._enabled(!1,!1)&&(a=!0);else if(5===s)break;return a}var l,u=e._startTime+_,p=[],f=0,c=0===e._duration;for(n=r.length;--n>-1;)(o=r[n])===e||o._gc||o._paused||(o._timeline!==e._timeline?(l=l||Q(e,0,c),0===Q(o,l,c)&&(p[f++]=o)):u>=o._startTime&&o._startTime+o.totalDuration()/o._timeScale>u&&((c||!o._initted)&&2e-10>=u-o._startTime||(p[f++]=o)));for(n=f;--n>-1;)if(o=p[n],2===s&&o._kill(i,t,e)&&(a=!0),2!==s||!o._firstPT&&o._initted){if(2!==s&&!W(o,e))continue;o._enabled(!1,!1)&&(a=!0)}return a},Q=function(t,e,i){for(var s=t._timeline,r=s._timeScale,n=t._startTime;s._timeline;){if(n+=s._startTime,r*=s._timeScale,s._paused)return-100;s=s._timeline}return n/=r,n>e?n-e:i&&n===e||!t._initted&&2*_>n-e?_:(n+=t.totalDuration()/t._timeScale/r)>e+_?0:n-e-_};n._init=function(){var t,e,i,s,r,n=this.vars,a=this._overwrittenProps,o=this._duration,h=!!n.immediateRender,l=n.ease;if(n.startAt){this._startAt&&(this._startAt.render(-1,!0),this._startAt.kill()),r={};for(s in n.startAt)r[s]=n.startAt[s];if(r.overwrite=!1,r.immediateRender=!0,r.lazy=h&&n.lazy!==!1,r.startAt=r.delay=null,this._startAt=D.to(this.target,0,r),h)if(this._time>0)this._startAt=null;else if(0!==o)return}else if(n.runBackwards&&0!==o)if(this._startAt)this._startAt.render(-1,!0),this._startAt.kill(),this._startAt=null;else{0!==this._time&&(h=!1),i={};for(s in n)U[s]&&"autoCSS"!==s||(i[s]=n[s]);if(i.overwrite=0,i.data="isFromStart",i.lazy=h&&n.lazy!==!1,i.immediateRender=h,this._startAt=D.to(this.target,0,i),h){if(0===this._time)return}else this._startAt._init(),this._startAt._enabled(!1),this.vars.immediateRender&&(this._startAt=null)}if(this._ease=l=l?l instanceof T?l:"function"==typeof l?new T(l,n.easeParams):w[l]||D.defaultEase:D.defaultEase,n.easeParams instanceof Array&&l.config&&(this._ease=l.config.apply(l,n.easeParams)),this._easeType=this._ease._type,this._easePower=this._ease._power,this._firstPT=null,this._targets)for(t=this._targets.length;--t>-1;)this._initProps(this._targets[t],this._propLookup[t]={},this._siblings[t],a?a[t]:null)&&(e=!0);else e=this._initProps(this.target,this._propLookup,this._siblings,a);if(e&&D._onPluginEvent("_onInitAllProps",this),a&&(this._firstPT||"function"!=typeof this.target&&this._enabled(!1,!1)),n.runBackwards)for(i=this._firstPT;i;)i.s+=i.c,i.c=-i.c,i=i._next;this._onUpdate=n.onUpdate,this._initted=!0},n._initProps=function(e,i,s,r){var n,a,o,h,l,_;if(null==e)return!1;F[e._gsTweenID]&&V(),this.vars.css||e.style&&e!==t&&e.nodeType&&N.css&&this.vars.autoCSS!==!1&&z(this.vars,e);for(n in this.vars){if(_=this.vars[n],U[n])_&&(_ instanceof Array||_.push&&f(_))&&-1!==_.join("").indexOf("{self}")&&(this.vars[n]=_=this._swapSelfInParams(_,this));else if(N[n]&&(h=new N[n])._onInitTween(e,this.vars[n],this)){for(this._firstPT=l={_next:this._firstPT,t:h,p:"setRatio",s:0,c:1,f:!0,n:n,pg:!0,pr:h._priority},a=h._overwriteProps.length;--a>-1;)i[h._overwriteProps[a]]=this._firstPT;(h._priority||h._onInitAllProps)&&(o=!0),(h._onDisable||h._onEnable)&&(this._notifyPluginsOfEnabled=!0)}else this._firstPT=i[n]=l={_next:this._firstPT,t:e,p:n,f:"function"==typeof e[n],n:n,pg:!1,pr:0},l.s=l.f?e[n.indexOf("set")||"function"!=typeof e["get"+n.substr(3)]?n:"get"+n.substr(3)]():parseFloat(e[n]),l.c="string"==typeof _&&"="===_.charAt(1)?parseInt(_.charAt(0)+"1",10)*Number(_.substr(2)):Number(_)-l.s||0;l&&l._next&&(l._next._prev=l)}return r&&this._kill(r,e)?this._initProps(e,i,s,r):this._overwrite>1&&this._firstPT&&s.length>1&&Z(e,this,i,this._overwrite,s)?(this._kill(i,e),this._initProps(e,i,s,r)):(this._firstPT&&(this.vars.lazy!==!1&&this._duration||this.vars.lazy&&!this._duration)&&(F[e._gsTweenID]=!0),o)},n.render=function(t,e,i){var s,r,n,a,o=this._time,h=this._duration,l=this._rawPrevTime;if(t>=h)this._totalTime=this._time=h,this.ratio=this._ease._calcEnd?this._ease.getRatio(1):1,this._reversed||(s=!0,r="onComplete"),0===h&&(this._initted||!this.vars.lazy||i)&&(this._startTime===this._timeline._duration&&(t=0),(0===t||0>l||l===_&&"isPause"!==this.data)&&l!==t&&(i=!0,l>_&&(r="onReverseComplete")),this._rawPrevTime=a=!e||t||l===t?t:_);else if(1e-7>t)this._totalTime=this._time=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0,(0!==o||0===h&&l>0)&&(r="onReverseComplete",s=this._reversed),0>t&&(this._active=!1,0===h&&(this._initted||!this.vars.lazy||i)&&(l>=0&&(l!==_||"isPause"!==this.data)&&(i=!0),this._rawPrevTime=a=!e||t||l===t?t:_)),this._initted||(i=!0); 1728else if(this._totalTime=this._time=t,this._easeType){var u=t/h,p=this._easeType,f=this._easePower;(1===p||3===p&&u>=.5)&&(u=1-u),3===p&&(u*=2),1===f?u*=u:2===f?u*=u*u:3===f?u*=u*u*u:4===f&&(u*=u*u*u*u),this.ratio=1===p?1-u:2===p?u:.5>t/h?u/2:1-u/2}else this.ratio=this._ease.getRatio(t/h);if(this._time!==o||i){if(!this._initted){if(this._init(),!this._initted||this._gc)return;if(!i&&this._firstPT&&(this.vars.lazy!==!1&&this._duration||this.vars.lazy&&!this._duration))return this._time=this._totalTime=o,this._rawPrevTime=l,I.push(this),this._lazy=[t,e],void 0;this._time&&!s?this.ratio=this._ease.getRatio(this._time/h):s&&this._ease._calcEnd&&(this.ratio=this._ease.getRatio(0===this._time?0:1))}for(this._lazy!==!1&&(this._lazy=!1),this._active||!this._paused&&this._time!==o&&t>=0&&(this._active=!0),0===o&&(this._startAt&&(t>=0?this._startAt.render(t,e,i):r||(r="_dummyGS")),this.vars.onStart&&(0!==this._time||0===h)&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||y))),n=this._firstPT;n;)n.f?n.t[n.p](n.c*this.ratio+n.s):n.t[n.p]=n.c*this.ratio+n.s,n=n._next;this._onUpdate&&(0>t&&this._startAt&&t!==-1e-4&&this._startAt.render(t,e,i),e||(this._time!==o||s)&&this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||y)),r&&(!this._gc||i)&&(0>t&&this._startAt&&!this._onUpdate&&t!==-1e-4&&this._startAt.render(t,e,i),s&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[r]&&this.vars[r].apply(this.vars[r+"Scope"]||this,this.vars[r+"Params"]||y),0===h&&this._rawPrevTime===_&&a!==_&&(this._rawPrevTime=0))}},n._kill=function(t,e,i){if("all"===t&&(t=null),null==t&&(null==e||e===this.target))return this._lazy=!1,this._enabled(!1,!1);e="string"!=typeof e?e||this._targets||this.target:D.selector(e)||e;var s,r,n,a,o,h,l,_,u;if((f(e)||M(e))&&"number"!=typeof e[0])for(s=e.length;--s>-1;)this._kill(t,e[s])&&(h=!0);else{if(this._targets){for(s=this._targets.length;--s>-1;)if(e===this._targets[s]){o=this._propLookup[s]||{},this._overwrittenProps=this._overwrittenProps||[],r=this._overwrittenProps[s]=t?this._overwrittenProps[s]||{}:"all";break}}else{if(e!==this.target)return!1;o=this._propLookup,r=this._overwrittenProps=t?this._overwrittenProps||{}:"all"}if(o){if(l=t||o,_=t!==r&&"all"!==r&&t!==o&&("object"!=typeof t||!t._tempKill),i&&(D.onOverwrite||this.vars.onOverwrite)){for(n in l)o[n]&&(u||(u=[]),u.push(n));if(!W(this,i,e,u))return!1}for(n in l)(a=o[n])&&(a.pg&&a.t._kill(l)&&(h=!0),a.pg&&0!==a.t._overwriteProps.length||(a._prev?a._prev._next=a._next:a===this._firstPT&&(this._firstPT=a._next),a._next&&(a._next._prev=a._prev),a._next=a._prev=null),delete o[n]),_&&(r[n]=1);!this._firstPT&&this._initted&&this._enabled(!1,!1)}}return h},n.invalidate=function(){return this._notifyPluginsOfEnabled&&D._onPluginEvent("_onDisable",this),this._firstPT=this._overwrittenProps=this._startAt=this._onUpdate=null,this._notifyPluginsOfEnabled=this._active=this._lazy=!1,this._propLookup=this._targets?{}:[],A.prototype.invalidate.call(this),this.vars.immediateRender&&(this._time=-_,this.render(-this._delay)),this},n._enabled=function(t,e){if(o||a.wake(),t&&this._gc){var i,s=this._targets;if(s)for(i=s.length;--i>-1;)this._siblings[i]=G(s[i],this,!0);else this._siblings=G(this.target,this,!0)}return A.prototype._enabled.call(this,t,e),this._notifyPluginsOfEnabled&&this._firstPT?D._onPluginEvent(t?"_onEnable":"_onDisable",this):!1},D.to=function(t,e,i){return new D(t,e,i)},D.from=function(t,e,i){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,new D(t,e,i)},D.fromTo=function(t,e,i,s){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,new D(t,e,s)},D.delayedCall=function(t,e,i,s,r){return new D(e,0,{delay:t,onComplete:e,onCompleteParams:i,onCompleteScope:s,onReverseComplete:e,onReverseCompleteParams:i,onReverseCompleteScope:s,immediateRender:!1,lazy:!1,useFrames:r,overwrite:0})},D.set=function(t,e){return new D(t,0,e)},D.getTweensOf=function(t,e){if(null==t)return[];t="string"!=typeof t?t:D.selector(t)||t;var i,s,r,n;if((f(t)||M(t))&&"number"!=typeof t[0]){for(i=t.length,s=[];--i>-1;)s=s.concat(D.getTweensOf(t[i],e));for(i=s.length;--i>-1;)for(n=s[i],r=i;--r>-1;)n===s[r]&&s.splice(i,1)}else for(s=G(t).concat(),i=s.length;--i>-1;)(s[i]._gc||e&&!s[i].isActive())&&s.splice(i,1);return s}
1728,D.killTweensOf=D.killDelayedCallsTo=function(t,e,i){"object"==typeof e&&(i=e,e=!1);for(var s=D.getTweensOf(t,e),r=s.length;--r>-1;)s[r]._kill(i,t)};var $=g("plugins.TweenPlugin",function(t,e){this._overwriteProps=(t||"").split(","),this._propName=this._overwriteProps[0],this._priority=e||0,this._super=$.prototype},!0);if(n=$.prototype,$.version="1.10.1",$.API=2,n._firstPT=null,n._addTween=function(t,e,i,s,r,n){var a,o;return null!=s&&(a="number"==typeof s||"="!==s.charAt(1)?Number(s)-i:parseInt(s.charAt(0)+"1",10)*Number(s.substr(2)))?(this._firstPT=o={_next:this._firstPT,t:t,p:e,s:i,c:a,f:"function"==typeof t[e],n:r||e,r:n},o._next&&(o._next._prev=o),o):void 0},n.setRatio=function(t){for(var e,i=this._firstPT,s=1e-6;i;)e=i.c*t+i.s,i.r?e=Math.round(e):s>e&&e>-s&&(e=0),i.f?i.t[i.p](e):i.t[i.p]=e,i=i._next},n._kill=function(t){var e,i=this._overwriteProps,s=this._firstPT;if(null!=t[this._propName])this._overwriteProps=[];else for(e=i.length;--e>-1;)null!=t[i[e]]&&i.splice(e,1);for(;s;)null!=t[s.n]&&(s._next&&(s._next._prev=s._prev),s._prev?(s._prev._next=s._next,s._prev=null):this._firstPT===s&&(this._firstPT=s._next)),s=s._next;return!1},n._roundProps=function(t,e){for(var i=this._firstPT;i;)(t[this._propName]||null!=i.n&&t[i.n.split(this._propName+"_").join("")])&&(i.r=e),i=i._next},D._onPluginEvent=function(t,e){var i,s,r,n,a,o=e._firstPT;if("_onInitAllProps"===t){for(;o;){for(a=o._next,s=r;s&&s.pr>o.pr;)s=s._next;(o._prev=s?s._prev:n)?o._prev._next=o:r=o,(o._next=s)?s._prev=o:n=o,o=a}o=e._firstPT=r}for(;o;)o.pg&&"function"==typeof o.t[t]&&o.t[t]()&&(i=!0),o=o._next;return i},$.activate=function(t){for(var e=t.length;--e>-1;)t[e].API===$.API&&(N[(new t[e])._propName]=t[e]);return!0},d.plugin=function(t){if(!(t&&t.propName&&t.init&&t.API))throw"illegal plugin definition.";var e,i=t.propName,s=t.priority||0,r=t.overwriteProps,n={init:"_onInitTween",set:"setRatio",kill:"_kill",round:"_roundProps",initAll:"_onInitAllProps"},a=g("plugins."+i.charAt(0).toUpperCase()+i.substr(1)+"Plugin",function(){$.call(this,i,s),this._overwriteProps=r||[]},t.global===!0),o=a.prototype=new $(i);o.constructor=a,a.API=t.API;for(e in n)"function"==typeof t[e]&&(o[n[e]]=t[e]);return a.version=t.version,$.activate([a]),a},s=t._gsQueue){for(r=0;s.length>r;r++)s[r]();for(n in c)c[n].func||t.console.log("GSAP encountered missing dependency: com.greensock."+n)}o=!1}}("undefined"!=typeof module&&module.exports&&"undefined"!=typeof global?global:this||window,"TweenMax"); 1729// Avoid `console` errors in browsers that lack a console. 1730(function () { 1731 var method; 1732 var noop = function noop() { }; 1733 var methods = [ 1734 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 1735 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 1736 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 1737 'timeStamp', 'trace', 'warn' 1738 ]; 1739 var length = methods.length; 1740 var console = (window.console = window.console || {}); 1741 while (length--) { 1742 method = methods[length]; 1743 // Only stub undefined methods. 1744 if (!console[method]) { 1745 console[method] = noop; 1746 } 1747 } 1748}()); 1749var Helper = new function () { 1750 // Has browser placeholder support 1751 this.nativePlaceholderSupport = function () { 1752 var i = document.createElement('input'); 1753 return ('placeholder' in i); 1754 }; 1755 //, other helper functions 1756}; 1757//DONT ADD JQUERY PLUGINS HERE!!!! 1758//# sourceMappingURL=plugins.js.map 1759/*! 1760 * jQuery Validation Plugin v1.13.1 1761 * 1762 * http://jqueryvalidation.org/ 1763 * 1764 * Copyright (c) 2014 Jörn Zaefferer 1765 * Released under the MIT license 1766 */ 1767(function( factory ) { 1768 if ( typeof define === "function" && define.amd ) { 1769 define( ["jquery"], factory ); 1770 } else { 1771 factory( jQuery ); 1772 } 1773}(function( $ ) { 1774 1775$.extend($.fn, { 1776 // http://jqueryvalidation.org/validate/ 1777 validate: function( options ) { 1778 1779 // if nothing is selected, return nothing; can't chain anyway 1780 if ( !this.length ) { 1781 if ( options && options.debug && window.console ) { 1782 console.warn( "Nothing selected, can't validate, returning nothing." ); 1783 } 1784 return; 1785 } 1786 1787 // check if a validator for this form was already created
1788 var validator = $.data( this[ 0 ], "validator" ); 1789 if ( validator ) { 1790 return validator; 1791 } 1792 1793 // Add novalidate tag if HTML5. 1794 this.attr( "novalidate", "novalidate" ); 1795 1796 validator = new $.validator( options, this[ 0 ] ); 1797 $.data( this[ 0 ], "validator", validator ); 1798 1799 if ( validator.settings.onsubmit ) { 1800 1801 this.validateDelegate( ":submit", "click", function( event ) { 1802 if ( validator.settings.submitHandler ) { 1803 validator.submitButton = event.target; 1804 } 1805 // allow suppressing validation by adding a cancel class to the submit button 1806 if ( $( event.target ).hasClass( "cancel" ) ) { 1807 validator.cancelSubmit = true; 1808 } 1809 1810 // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button 1811 if ( $( event.target ).attr( "formnovalidate" ) !== undefined ) { 1812 validator.cancelSubmit = true; 1813 } 1814 }); 1815 1816 // validate the form on submit 1817 this.submit( function( event ) { 1818 if ( validator.settings.debug ) { 1819 // prevent form submit to be able to see console output 1820 event.preventDefault(); 1821 } 1822 function handle() { 1823 var hidden, result; 1824 if ( validator.settings.submitHandler ) { 1825 if ( validator.submitButton ) { 1826 // insert a hidden input as a replacement for the missing submit button 1827 hidden = $( "<input type='hidden'/>" ) 1828 .attr( "name", validator.submitButton.name ) 1829 .val( $( validator.submitButton ).val() ) 1830 .appendTo( validator.currentForm ); 1831 } 1832 result = validator.settings.submitHandler.call( validator, validator.currentForm, event ); 1833 if ( validator.submitButton ) { 1834 // and clean up afterwards; thanks to no-block-scope, hidden can be referenced 1835 hidden.remove(); 1836 } 1837 if ( result !== undefined ) { 1838 return result; 1839 } 1840 return false; 1841 } 1842 return true; 1843 } 1844 1845 // prevent submit for invalid forms or custom submit handlers 1846 if ( validator.cancelSubmit ) { 1847 validator.cancelSubmit = false; 1848 return handle(); 1849 } 1850 if ( validator.form() ) { 1851 if ( validator.pendingRequest ) { 1852 validator.formSubmitted = true; 1853 return false; 1854 } 1855 return handle(); 1856 } else { 1857 validator.focusInvalid(); 1858 return false; 1859 } 1860 }); 1861 } 1862 1863 return validator; 1864 }, 1865 // http://jqueryvalidation.org/valid/ 1866 valid: function() { 1867 var valid, validator; 1868 1869 if ( $( this[ 0 ] ).is( "form" ) ) { 1870 valid = this.validate().form(); 1871 } else { 1872 valid = true; 1873 validator = $( this[ 0 ].form ).validate(); 1874 this.each( function() { 1875 valid = validator.element( this ) && valid; 1876 }); 1877 } 1878 return valid; 1879 }, 1880 // attributes: space separated list of attributes to retrieve and remove 1881 removeAttrs: function( attributes ) { 1882 var result = {}, 1883 $element = this; 1884 $.each( attributes.split( /\s/ ), function( index, value ) { 1885 result[ value ] = $element.attr( value ); 1886 $element.removeAttr( value ); 1887 }); 1888 return result; 1889 }, 1890 // http://jqueryvalidation.org/rules/ 1891 rules: function( command, argument ) { 1892 var element = this[ 0 ], 1893 settings, staticRules, existingRules, data, param, filtered; 1894 1895 if ( command ) { 1896 settings = $.data( element.form, "validator" ).settings; 1897 staticRules = settings.rules; 1898 existingRules = $.validator.staticRules( element ); 1899 switch ( command ) { 1900 case "add": 1901 $.extend( existingRules, $.validator.normalizeRule( argument ) ); 1902 // remove messages from rules, but allow them to be set separately 1903 delete existingRules.messages; 1904 staticRules[ element.name ] = existingRules; 1905 if ( argument.messages ) { 1906 settings.messages[ element.name ] = $.extend( settings.messages[ element.name ], argument.messages ); 1907 } 1908 break; 1909 case "remove": 1910 if ( !argument ) { 1911 delete staticRules[ element.name ]; 1912 return existingRules; 1913 } 1914 filtered = {}; 1915 $.each( argument.split( /\s/ ), function( index, method ) { 1916 filtered[ method ] = existingRules[ method ]; 1917 delete existingRules[ method ]; 1918 if ( method === "required" ) { 1919 $( element ).removeAttr( "aria-required" ); 1920 } 1921 }); 1922 return filtered; 1923 } 1924 } 1925 1926 data = $.validator.normalizeRules( 1927 $.extend( 1928 {}, 1929 $.validator.classRules( element ), 1930 $.validator.attributeRules( element ), 1931 $.validator.dataRules( element ), 1932 $.validator.staticRules( element ) 1933 ), element ); 1934 1935 // make sure required is at front 1936 if ( data.required ) { 1937 param = data.required; 1938 delete data.required; 1939 data = $.extend( { required: param }, data ); 1940 $( element ).attr( "aria-required", "true" ); 1941 } 1942 1943 // make sure remote is at back 1944 if ( data.remote ) { 1945 param = data.remote; 1946 delete data.remote; 1947 data = $.extend( data, { remote: param }); 1948 } 1949 1950 return data; 1951 } 1952}); 1953 1954// Custom selectors 1955$.extend( $.expr[ ":" ], { 1956 // http://jqueryvalidation.org/blank-selector/ 1957 blank: function( a ) { 1958 return !$.trim( "" + $( a ).val() ); 1959 }, 1960 // http://jqueryvalidation.org/filled-selector/ 1961 filled: function( a ) { 1962 return !!$.trim( "" + $( a ).val() ); 1963 }, 1964 // http://jqueryvalidation.org/unchecked-selector/ 1965 unchecked: function( a ) { 1966 return !$( a ).prop( "checked" ); 1967 } 1968}); 1969 1970// constructor for validator 1971$.validator = function( options, form ) { 1972 this.settings = $.extend( true, {}, $.validator.defaults, options ); 1973 this.currentForm = form; 1974 this.init(); 1975}; 1976 1977// http://jqueryvalidation.org/jQuery.validator.format/ 1978$.validator.format = function( source, params ) { 1979 if ( arguments.length === 1 ) { 1980 return function() { 1981 var args = $.makeArray( arguments ); 1982 args.unshift( source ); 1983 return $.validator.format.apply( this, args ); 1984 }; 1985 } 1986 if ( arguments.length > 2 && params.constructor !== Array ) { 1987 params = $.makeArray( arguments ).slice( 1 ); 1988 } 1989 if ( params.constructor !== Array ) { 1990 params = [ params ]; 1991 } 1992 $.each( params, function( i, n ) { 1993 source = source.replace( new RegExp( "\\{" + i + "\\}", "g" ), function() { 1994 return n; 1995 }); 1996 }); 1997 return source; 1998}; 1999 2000$.extend( $.validator, { 2001 2002 defaults: { 2003 messages: {}, 2004 groups: {}, 2005 rules: {}, 2006 errorClass: "error", 2007 validClass: "valid", 2008 errorElement: "label", 2009 focusCleanup: false, 2010 focusInvalid: true, 2011 errorContainer: $( [] ), 2012 errorLabelContainer: $( [] ), 2013 onsubmit: true, 2014 ignore: ":hidden", 2015 ignoreTitle: false, 2016 onfocusin: function( element ) { 2017 this.lastActive = element; 2018 2019 // Hide error label and remove error class on focus if enabled 2020 if ( this.settings.focusCleanup ) { 2021 if ( this.settings.unhighlight ) { 2022 this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.val
2022idClass ); 2023 } 2024 this.hideThese( this.errorsFor( element ) ); 2025 } 2026 }, 2027 onfocusout: function( element ) { 2028 if ( !this.checkable( element ) && ( element.name in this.submitted || !this.optional( element ) ) ) { 2029 this.element( element ); 2030 } 2031 }, 2032 onkeyup: function( element, event ) { 2033 if ( event.which === 9 && this.elementValue( element ) === "" ) { 2034 return; 2035 } else if ( element.name in this.submitted || element === this.lastElement ) { 2036 this.element( element ); 2037 } 2038 }, 2039 onclick: function( element ) { 2040 // click on selects, radiobuttons and checkboxes 2041 if ( element.name in this.submitted ) { 2042 this.element( element ); 2043 2044 // or option elements, check parent select in that case 2045 } else if ( element.parentNode.name in this.submitted ) { 2046 this.element( element.parentNode ); 2047 } 2048 }, 2049 highlight: function( element, errorClass, validClass ) { 2050 if ( element.type === "radio" ) { 2051 this.findByName( element.name ).addClass( errorClass ).removeClass( validClass ); 2052 } else { 2053 $( element ).addClass( errorClass ).removeClass( validClass ); 2054 } 2055 }, 2056 unhighlight: function( element, errorClass, validClass ) { 2057 if ( element.type === "radio" ) { 2058 this.findByName( element.name ).removeClass( errorClass ).addClass( validClass ); 2059 } else { 2060 $( element ).removeClass( errorClass ).addClass( validClass ); 2061 } 2062 } 2063 }, 2064 2065 // http://jqueryvalidation.org/jQuery.validator.setDefaults/ 2066 setDefaults: function( settings ) { 2067 $.extend( $.validator.defaults, settings ); 2068 }, 2069 2070 messages: { 2071 required: "This field is required.", 2072 remote: "Please fix this field.", 2073 email: "Please enter a valid email address.", 2074 url: "Please enter a valid URL.", 2075 date: "Please enter a valid date.", 2076 dateISO: "Please enter a valid date ( ISO ).", 2077 number: "Please enter a valid number.", 2078 digits: "Please enter only digits.", 2079 creditcard: "Please enter a valid credit card number.", 2080 equalTo: "Please enter the same value again.", 2081 maxlength: $.validator.format( "Please enter no more than {0} characters." ), 2082 minlength: $.validator.format( "Please enter at least {0} characters." ), 2083 rangelength: $.validator.format( "Please enter a value between {0} and {1} characters long." ), 2084 range: $.validator.format( "Please enter a value between {0} and {1}." ), 2085 max: $.validator.format( "Please enter a value less than or equal to {0}." ), 2086 min: $.validator.format( "Please enter a value greater than or equal to {0}." ) 2087 }, 2088 2089 autoCreateRanges: false, 2090 2091 prototype: { 2092 2093 init: function() { 2094 this.labelContainer = $( this.settings.errorLabelContainer ); 2095 this.errorContext = this.labelContainer.length && this.labelContainer || $( this.currentForm ); 2096 this.containers = $( this.settings.errorContainer ).add( this.settings.errorLabelContainer ); 2097 this.submitted = {}; 2098 this.valueCache = {}; 2099 this.pendingRequest = 0; 2100 this.pending = {}; 2101 this.invalid = {}; 2102 this.reset(); 2103 2104 var groups = ( this.groups = {} ), 2105 rules; 2106 $.each( this.settings.groups, function( key, value ) { 2107 if ( typeof value === "string" ) { 2108 value = value.split( /\s/ ); 2109 } 2110 $.each( value, function( index, name ) { 2111 groups[ name ] = key; 2112 }); 2113 }); 2114 rules = this.settings.rules; 2115 $.each( rules, function( key, value ) { 2116 rules[ key ] = $.validator.normalizeRule( value ); 2117 }); 2118 2119 function delegate( event ) { 2120 var validator = $.data( this[ 0 ].form, "validator" ), 2121 eventType = "on" + event.type.replace( /^validate/, "" ), 2122 settings = validator.settings; 2123 if ( settings[ eventType ] && !this.is( settings.ignore ) ) { 2124 settings[ eventType ].call( validator, this[ 0 ], event ); 2125 } 2126 } 2127 $( this.currentForm ) 2128 .validateDelegate( ":text, [type='password'], [type='file'], select, textarea, " + 2129 "[type='number'], [type='search'] ,[type='tel'], [type='url'], " + 2130 "[type='email'], [type='datetime'], [type='date'], [type='month'], " + 2131 "[type='week'], [type='time'], [type='datetime-local'], " + 2132 "[type='range'], [type='color'], [type='radio'], [type='checkbox']", 2133 "focusin focusout keyup", delegate)
2134 // Support: Chrome, oldIE 2135 // "select" is provided as event.target when clicking a option 2136 .validateDelegate("select, option, [type='radio'], [type='checkbox']", "click", delegate); 2137 2138 if ( this.settings.invalidHandler ) { 2139 $( this.currentForm ).bind( "invalid-form.validate", this.settings.invalidHandler ); 2140 } 2141 2142 // Add aria-required to any Static/Data/Class required fields before first validation 2143 // Screen readers require this attribute to be present before the initial submission http://www.w3.org/TR/WCAG-TECHS/ARIA2.html 2144 $( this.currentForm ).find( "[required], [data-rule-required], .required" ).attr( "aria-required", "true" ); 2145 }, 2146 2147 // http://jqueryvalidation.org/Validator.form/ 2148 form: function() { 2149 this.checkForm(); 2150 $.extend( this.submitted, this.errorMap ); 2151 this.invalid = $.extend({}, this.errorMap ); 2152 if ( !this.valid() ) { 2153 $( this.currentForm ).triggerHandler( "invalid-form", [ this ]); 2154 } 2155 this.showErrors(); 2156 return this.valid(); 2157 }, 2158 2159 checkForm: function() { 2160 this.prepareForm(); 2161 for ( var i = 0, elements = ( this.currentElements = this.elements() ); elements[ i ]; i++ ) { 2162 this.check( elements[ i ] ); 2163 } 2164 return this.valid(); 2165 }, 2166 2167 // http://jqueryvalidation.org/Validator.element/ 2168 element: function( element ) { 2169 var cleanElement = this.clean( element ), 2170 checkElement = this.validationTargetFor( cleanElement ), 2171 result = true; 2172 2173 this.lastElement = checkElement; 2174 2175 if ( checkElement === undefined ) { 2176 delete this.invalid[ cleanElement.name ]; 2177 } else { 2178 this.prepareElement( checkElement ); 2179 this.currentElements = $( checkElement ); 2180 2181 result = this.check( checkElement ) !== false; 2182 if ( result ) { 2183 delete this.invalid[ checkElement.name ]; 2184 } else { 2185 this.invalid[ checkElement.name ] = true; 2186 } 2187 } 2188 // Add aria-invalid status for screen readers 2189 $( element ).attr( "aria-invalid", !result ); 2190 2191 if ( !this.numberOfInvalids() ) { 2192 // Hide error containers on last error 2193 this.toHide = this.toHide.add( this.containers ); 2194 } 2195 this.showErrors(); 2196 return result; 2197 }, 2198 2199 // http://jqueryvalidation.org/Validator.showErrors/ 2200 showErrors: function( errors ) { 2201 if ( errors ) { 2202 // add items to error list and map 2203 $.extend( this.errorMap, errors ); 2204 this.errorList = []; 2205 for ( var name in errors ) { 2206 this.errorList.push({ 2207 message: errors[ name ], 2208 element: this.findByName( name )[ 0 ] 2209 }); 2210 } 2211 // remove items from success list 2212 this.successList = $.grep( this.successList, function( element ) { 2213 return !( element.name in errors ); 2214 }); 2215 } 2216 if ( this.settings.showErrors ) { 2217 this.settings.showErrors.call( this, this.errorMap, this.errorList ); 2218 } else { 2219 this.defaultShowErrors(); 2220 } 2221 }, 2222 2223 // http://jqueryvalidation.org/Validator.resetForm/ 2224 resetForm: function() { 2225 if ( $.fn.resetForm ) { 2226 $( this.currentForm ).resetForm(); 2227 } 2228 this.submitted = {}; 2229 this.lastElement = null; 2230 this.prepareForm(); 2231 this.hideErrors(); 2232 this.elements() 2233 .removeClass( this.settings.errorClass ) 2234 .removeData( "previousValue" ) 2235 .removeAttr( "aria-invalid" ); 2236 }, 2237 2238 numberOfInvalids: function() { 2239 return this.objectLength( this.invalid ); 2240 }, 2241 2242 objectLength: function( obj ) { 2243 /* jshint unused: false */ 2244 var count = 0, 2245 i; 2246 for ( i in obj ) { 2247 count++; 2248 } 2249 return count; 2250 }, 2251 2252 hideErrors: function() { 2253 this.hideThese( this.toHide ); 2254 }, 2255 2256 hideThese: function( errors ) { 2257 errors.not( this.containers ).text( "" ); 2258 this.addWrapper( errors ).hide(); 2259 }, 2260 2261 valid: function() { 2262 return this.size() === 0; 2263 }, 2264 2265 size: function() { 2266 return this.errorList.length; 2267 }, 2268 2269 focusInvalid: function() { 2270 if ( this.settings.focusInvalid ) { 2271 try { 2272 $( this.findLastActive() || this.errorList.length && this.errorList[ 0 ].element || []) 2273 .filter( ":visible" ) 2274 .focus() 2275 // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find 2276 .trigger( "focusin" ); 2277 } catch ( e ) {
2278 // ignore IE throwing errors when focusing hidden elements 2279 } 2280 } 2281 }, 2282 2283 findLastActive: function() { 2284 var lastActive = this.lastActive; 2285 return lastActive && $.grep( this.errorList, function( n ) { 2286 return n.element.name === lastActive.name; 2287 }).length === 1 && lastActive; 2288 }, 2289 2290 elements: function() { 2291 var validator = this, 2292 rulesCache = {}; 2293 2294 // select all valid inputs inside the form (no submit or reset buttons) 2295 return $( this.currentForm ) 2296 .find( "input, select, textarea" ) 2297 .not( ":submit, :reset, :image, [disabled], [readonly]" ) 2298 .not( this.settings.ignore ) 2299 .filter( function() { 2300 if ( !this.name && validator.settings.debug && window.console ) { 2301 console.error( "%o has no name assigned", this ); 2302 } 2303 2304 // select only the first element for each name, and only those with rules specified 2305 if ( this.name in rulesCache || !validator.objectLength( $( this ).rules() ) ) { 2306 return false; 2307 } 2308 2309 rulesCache[ this.name ] = true; 2310 return true; 2311 }); 2312 }, 2313 2314 clean: function( selector ) { 2315 return $( selector )[ 0 ]; 2316 }, 2317 2318 errors: function() { 2319 var errorClass = this.settings.errorClass.split( " " ).join( "." ); 2320 return $( this.settings.errorElement + "." + errorClass, this.errorContext ); 2321 }, 2322 2323 reset: function() { 2324 this.successList = []; 2325 this.errorList = []; 2326 this.errorMap = {}; 2327 this.toShow = $( [] ); 2328 this.toHide = $( [] ); 2329 this.currentElements = $( [] ); 2330 }, 2331 2332 prepareForm: function() { 2333 this.reset(); 2334 this.toHide = this.errors().add( this.containers ); 2335 }, 2336 2337 prepareElement: function( element ) { 2338 this.reset(); 2339 this.toHide = this.errorsFor( element ); 2340 }, 2341 2342 elementValue: function( element ) { 2343 var val, 2344 $element = $( element ), 2345 type = element.type; 2346 2347 if ( type === "radio" || type === "checkbox" ) { 2348 return $( "input[name='" + element.name + "']:checked" ).val(); 2349 } else if ( type === "number" && typeof element.validity !== "undefined" ) { 2350 return element.validity.badInput ? false : $element.val(); 2351 } 2352 2353 val = $element.val(); 2354 if ( typeof val === "string" ) { 2355 return val.replace(/\r/g, "" ); 2356 } 2357 return val; 2358 }, 2359 2360 check: function( element ) { 2361 element = this.validationTargetFor( this.clean( element ) ); 2362 2363 var rules = $( element ).rules(), 2364 rulesCount = $.map( rules, function( n, i ) { 2365 return i; 2366 }).length, 2367 dependencyMismatch = false, 2368 val = this.elementValue( element ), 2369 result, method, rule; 2370 2371 for ( method in rules ) { 2372 rule = { method: method, parameters: rules[ method ] }; 2373 try { 2374 2375 result = $.validator.methods[ method ].call( this, val, element, rule.parameters ); 2376 2377 // if a method indicates that the field is optional and therefore valid, 2378 // don't mark it as valid when there are no other rules 2379 if ( result === "dependency-mismatch" && rulesCount === 1 ) { 2380 dependencyMismatch = true; 2381 continue; 2382 } 2383 dependencyMismatch = false; 2384 2385 if ( result === "pending" ) { 2386 this.toHide = this.toHide.not( this.errorsFor( element ) ); 2387 return; 2388 } 2389 2390 if ( !result ) { 2391 this.formatAndAdd( element, rule ); 2392 return false; 2393 } 2394 } catch ( e ) { 2395 if ( this.settings.debug && window.console ) { 2396 console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); 2397 } 2398 throw e; 2399 } 2400 } 2401 if ( dependencyMismatch ) { 2402 return; 2403 } 2404 if ( this.objectLength( rules ) ) { 2405 this.successList.push( element ); 2406 } 2407 return true; 2408 }, 2409 2410 // return the custom message for the given element and validation method 2411 // specified in the element's HTML5 data attribute 2412 // return the generic message if present and no method specific message is present 2413 customDataMessage: function( element, method ) { 2414 return $( element ).data( "msg" + method.charAt( 0 ).toUpperCase() + 2415 method.substring( 1 ).toLowerCase() ) || $( element ).data( "msg" ); 2416 }, 2417 2418 // return the custom message for the given element name and validation method 2419 customMessage: function( name, method ) { 2420 var m = this.settings.messages[ name ]; 2421 return m && ( m.constructor === String ? m : m[ method ]); 2422 }, 2423 2424 // return the first defined argument, allowing empty strings 2425 findDefined: function() { 2426 for ( var i = 0; i < arguments.length; i++) { 2427 if ( arguments[ i ] !== undefined ) { 2428 return arguments[ i ]; 2429 } 2430 } 2431 return undefined; 2432 }, 2433 2434 defaultMessage: function( element, method ) { 2435 return this.findDefined( 2436 this.customMessage( element.name, method ), 2437 this.customDataMessage( element, method ), 2438 // title is never undefined, so handle empty string as undefined 2439 !this.settings.ignoreTitle && element.title || undefined, 2440 $.validator.messages[ method ], 2441 "<strong>Warning: No message defined for " + element.name + "</strong>" 2442 ); 2443 }, 2444 2445 formatAndAdd: function( element, rule ) { 2446 var message = this.defaultMessage( element, rule.method ), 2447 theregex = /\$?\{(\d+)\}/g; 2448 if ( typeof message === "function" ) { 2449 message = message.call( this, rule.parameters, element ); 2450 } else if ( theregex.test( message ) ) { 2451 message = $.validator.format( message.replace( theregex, "{$1}" ), rule.parameters ); 2452 } 2453 this.errorList.push({ 2454 message: message, 2455 element: element, 2456 method: rule.method 2457 }); 2458 2459 this.errorMap[ element.name ] = message; 2460 this.submitted[ element.name ] = message; 2461 }, 2462 2463 addWrapper: function( toToggle ) { 2464 if ( this.settings.wrapper ) { 2465 toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); 2466 } 2467 return toToggle; 2468 }, 2469 2470 defaultShowErrors: function() { 2471 var i, elements, error; 2472 for ( i = 0; this.errorList[ i ]; i++ ) { 2473 error = this.errorList[ i ]; 2474 if ( this.settings.highlight ) { 2475 this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.val
2475idClass ); 2476 } 2477 this.showLabel( error.element, error.message ); 2478 } 2479 if ( this.errorList.length ) { 2480 this.toShow = this.toShow.add( this.containers ); 2481 } 2482 if ( this.settings.success ) { 2483 for ( i = 0; this.successList[ i ]; i++ ) { 2484 this.showLabel( this.successList[ i ] ); 2485 } 2486 } 2487 if ( this.settings.unhighlight ) { 2488 for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) { 2489 this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass ); 2490 } 2491 } 2492 this.toHide = this.toHide.not( this.toShow ); 2493 this.hideErrors(); 2494 this.addWrapper( this.toShow ).show(); 2495 }, 2496 2497 validElements: function() { 2498 return this.currentElements.not( this.invalidElements() ); 2499 }, 2500 2501 invalidElements: function() { 2502 return $( this.errorList ).map(function() { 2503 return this.element; 2504 }); 2505 }, 2506 2507 showLabel: function( element, message ) { 2508 var place, group, errorID, 2509 error = this.errorsFor( element ), 2510 elementID = this.idOrName( element ), 2511 describedBy = $( element ).attr( "aria-describedby" ); 2512 if ( error.length ) { 2513 // refresh error/success class 2514 error.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); 2515 // replace message on existing label 2516 error.html( message ); 2517 } else { 2518 // create error element 2519 error = $( "<" + this.settings.errorElement + ">" ) 2520 .attr( "id", elementID + "-error" ) 2521 .addClass( this.settings.errorClass ) 2522 .html( message || "" ); 2523 2524 // Maintain reference to the element to be placed into the DOM 2525 place = error; 2526 if ( this.settings.wrapper ) { 2527 // make sure the element is visible, even in IE 2528 // actually showing the wrapped element is handled elsewhere 2529 place = error.hide().show().wrap( "<" + this.settings.wrapper + "/>" ).parent(); 2530 } 2531 if ( this.labelContainer.length ) { 2532 this.labelContainer.append( place ); 2533 } else if ( this.settings.errorPlacement ) { 2534 this.settings.errorPlacement( place, $( element ) ); 2535 } else { 2536 place.insertAfter( element ); 2537 } 2538 2539 // Link error back to the element 2540 if ( error.is( "label" ) ) { 2541 // If the error is a label, then associate using 'for' 2542 error.attr( "for", elementID ); 2543 } else if ( error.parents( "label[for='" + elementID + "']" ).length === 0 ) { 2544 // If the element is not a child of an associated label, then it's necessary 2545 // to explicitly apply aria-describedby 2546 2547 errorID = error.attr( "id" ).replace( /(:|\.|\[|\])/g, "\\$1"); 2548 // Respect existing non-error aria-describedby 2549 if ( !describedBy ) { 2550 describedBy = errorID; 2551 } else if ( !describedBy.match( new RegExp( "\\b" + errorID + "\\b" ) ) ) { 2552 // Add to end of list if not already present 2553 describedBy += " " + errorID; 2554 } 2555 $( element ).attr( "aria-describedby", describedBy ); 2556 2557 // If this element is grouped, then assign to all elements in the same group 2558 group = this.groups[ element.name ]; 2559 if ( group ) { 2560 $.each( this.groups, function( name, testgroup ) { 2561 if ( testgroup === group ) { 2562 $( "[name='" + name + "']", this.currentForm ) 2563 .attr( "aria-describedby", error.attr( "id" ) ); 2564 } 2565 }); 2566 } 2567 } 2568 } 2569 if ( !message && this.settings.success ) { 2570 error.text( "" ); 2571 if ( typeof this.settings.success === "string" ) { 2572 error.addClass( this.settings.success ); 2573 } else { 2574 this.settings.success( error, element ); 2575 } 2576 } 2577 this.toShow = this.toShow.add( error ); 2578 }, 2579 2580 errorsFor: function( element ) { 2581 var name = this.idOrName( element ), 2582 describer = $( element ).attr( "aria-describedby" ), 2583 selector = "label[for='" + name + "'], label[for='" + name + "'] *"; 2584 2585 // aria-describedby should directly reference the error element 2586 if ( describer ) { 2587 selector = selector + ", #" + describer.replace( /\s+/g, ", #" ); 2588 } 2589 return this 2590 .errors() 2591 .filter( selector ); 2592 }, 2593 2594 idOrName: function( element ) { 2595 return this.groups[ element.name ] || ( this.checkable( element ) ? element.name : element.i
vendor: 7,280 bytes, lines 2595-2848
2595d || element.name ); 2596 }, 2597 2598 validationTargetFor: function( element ) { 2599 2600 // If radio/checkbox, validate first element in group instead 2601 if ( this.checkable( element ) ) { 2602 element = this.findByName( element.name ); 2603 } 2604 2605 // Always apply ignore filter 2606 return $( element ).not( this.settings.ignore )[ 0 ]; 2607 }, 2608 2609 checkable: function( element ) { 2610 return ( /radio|checkbox/i ).test( element.type ); 2611 }, 2612 2613 findByName: function( name ) { 2614 return $( this.currentForm ).find( "[name='" + name + "']" ); 2615 }, 2616 2617 getLength: function( value, element ) { 2618 switch ( element.nodeName.toLowerCase() ) { 2619 case "select": 2620 return $( "option:selected", element ).length; 2621 case "input": 2622 if ( this.checkable( element ) ) { 2623 return this.findByName( element.name ).filter( ":checked" ).length; 2624 } 2625 } 2626 return value.length; 2627 }, 2628 2629 depend: function( param, element ) { 2630 return this.dependTypes[typeof param] ? this.dependTypes[typeof param]( param, element ) : true; 2631 }, 2632 2633 dependTypes: { 2634 "boolean": function( param ) { 2635 return param; 2636 }, 2637 "string": function( param, element ) { 2638 return !!$( param, element.form ).length; 2639 }, 2640 "function": function( param, element ) { 2641 return param( element ); 2642 } 2643 }, 2644 2645 optional: function( element ) { 2646 var val = this.elementValue( element ); 2647 return !$.validator.methods.required.call( this, val, element ) && "dependency-mismatch"; 2648 }, 2649 2650 startRequest: function( element ) { 2651 if ( !this.pending[ element.name ] ) { 2652 this.pendingRequest++; 2653 this.pending[ element.name ] = true; 2654 } 2655 }, 2656 2657 stopRequest: function( element, valid ) { 2658 this.pendingRequest--; 2659 // sometimes synchronization fails, make sure pendingRequest is never < 0 2660 if ( this.pendingRequest < 0 ) { 2661 this.pendingRequest = 0; 2662 } 2663 delete this.pending[ element.name ]; 2664 if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { 2665 $( this.currentForm ).submit(); 2666 this.formSubmitted = false; 2667 } else if (!valid && this.pendingRequest === 0 && this.formSubmitted ) { 2668 $( this.currentForm ).triggerHandler( "invalid-form", [ this ]); 2669 this.formSubmitted = false; 2670 } 2671 }, 2672 2673 previousValue: function( element ) { 2674 return $.data( element, "previousValue" ) || $.data( element, "previousValue", { 2675 old: null, 2676 valid: true, 2677 message: this.defaultMessage( element, "remote" ) 2678 }); 2679 } 2680 2681 }, 2682 2683 classRuleSettings: { 2684 required: { required: true }, 2685 email: { email: true }, 2686 url: { url: true }, 2687 date: { date: true }, 2688 dateISO: { dateISO: true }, 2689 number: { number: true }, 2690 digits: { digits: true }, 2691 creditcard: { creditcard: true } 2692 }, 2693 2694 addClassRules: function( className, rules ) { 2695 if ( className.constructor === String ) { 2696 this.classRuleSettings[ className ] = rules; 2697 } else { 2698 $.extend( this.classRuleSettings, className ); 2699 } 2700 }, 2701 2702 classRules: function( element ) { 2703 var rules = {}, 2704 classes = $( element ).attr( "class" ); 2705 2706 if ( classes ) { 2707 $.each( classes.split( " " ), function() { 2708 if ( this in $.validator.classRuleSettings ) { 2709 $.extend( rules, $.validator.classRuleSettings[ this ]); 2710 } 2711 }); 2712 } 2713 return rules; 2714 }, 2715 2716 attributeRules: function( element ) { 2717 var rules = {}, 2718 $element = $( element ), 2719 type = element.getAttribute( "type" ), 2720 method, value; 2721 2722 for ( method in $.validator.methods ) { 2723 2724 // support for <input required> in both html5 and older browsers 2725 if ( method === "required" ) { 2726 value = element.getAttribute( method ); 2727 // Some browsers return an empty string for the required attribute 2728 // and non-HTML5 browsers might have required="" markup 2729 if ( value === "" ) { 2730 value = true; 2731 } 2732 // force non-HTML5 browsers to return bool 2733 value = !!value; 2734 } else { 2735 value = $element.attr( method ); 2736 } 2737 2738 // convert the value to a number for number inputs, and for text for backwards compability 2739 // allows type="date" and others to be compared as strings 2740 if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) { 2741 value = Number( value ); 2742 } 2743 2744 if ( value || value === 0 ) { 2745 rules[ method ] = value; 2746 } else if ( type === method && type !== "range" ) { 2747 // exception: the jquery validate 'range' method 2748 // does not test for the html5 'range' type 2749 rules[ method ] = true; 2750 } 2751 } 2752 2753 // maxlength may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs 2754 if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) { 2755 delete rules.maxlength; 2756 } 2757 2758 return rules; 2759 }, 2760 2761 dataRules: function( element ) { 2762 var method, value, 2763 rules = {}, $element = $( element ); 2764 for ( method in $.validator.methods ) { 2765 value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() ); 2766 if ( value !== undefined ) { 2767 rules[ method ] = value; 2768 } 2769 } 2770 return rules; 2771 }, 2772 2773 staticRules: function( element ) { 2774 var rules = {}, 2775 validator = $.data( element.form, "validator" ); 2776 2777 if ( validator.settings.rules ) { 2778 rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {}; 2779 } 2780 return rules; 2781 }, 2782 2783 normalizeRules: function( rules, element ) { 2784 // handle dependency check 2785 $.each( rules, function( prop, val ) { 2786 // ignore rule when param is explicitly false, eg. required:false 2787 if ( val === false ) { 2788 delete rules[ prop ]; 2789 return; 2790 } 2791 if ( val.param || val.depends ) { 2792 var keepRule = true; 2793 switch ( typeof val.depends ) { 2794 case "string": 2795 keepRule = !!$( val.depends, element.form ).length; 2796 break; 2797 case "function": 2798 keepRule = val.depends.call( element, element ); 2799 break; 2800 } 2801 if ( keepRule ) { 2802 rules[ prop ] = val.param !== undefined ? val.param : true; 2803 } else { 2804 delete rules[ prop ]; 2805 } 2806 } 2807 }); 2808 2809 // evaluate parameters 2810 $.each( rules, function( rule, parameter ) { 2811 rules[ rule ] = $.isFunction( parameter ) ? parameter( element ) : parameter; 2812 }); 2813 2814 // clean number parameters 2815 $.each([ "minlength", "maxlength" ], function() { 2816 if ( rules[ this ] ) { 2817 rules[ this ] = Number( rules[ this ] ); 2818 } 2819 }); 2820 $.each([ "rangelength", "range" ], function() { 2821 var parts; 2822 if ( rules[ this ] ) { 2823 if ( $.isArray( rules[ this ] ) ) { 2824 rules[ this ] = [ Number( rules[ this ][ 0 ]), Number( rules[ this ][ 1 ] ) ]; 2825 } else if ( typeof rules[ this ] === "string" ) { 2826 parts = rules[ this ].replace(/[\[\]]/g, "" ).split( /[\s,]+/ ); 2827 rules[ this ] = [ Number( parts[ 0 ]), Number( parts[ 1 ] ) ]; 2828 } 2829 } 2830 }); 2831 2832 if ( $.validator.autoCreateRanges ) { 2833 // auto-create ranges 2834 if ( rules.min != null && rules.max != null ) { 2835 rules.range = [ rules.min, rules.max ]; 2836 delete rules.min; 2837 delete rules.max; 2838 } 2839 if ( rules.minlength != null && rules.maxlength != null ) { 2840 rules.rangelength = [ rules.minlength, rules.maxlength ]; 2841 delete rules.minlength; 2842 delete rules.maxlength; 2843 } 2844 } 2845 2846 return rules; 2847 }, 2848
2849 // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} 2850 normalizeRule: function( data ) { 2851 if ( typeof data === "string" ) { 2852 var transformed = {}; 2853 $.each( data.split( /\s/ ), function() { 2854 transformed[ this ] = true; 2855 }); 2856 data = transformed; 2857 } 2858 return data; 2859 }, 2860 2861 // http://jqueryvalidation.org/jQuery.validator.addMethod/ 2862 addMethod: function( name, method, message ) { 2863 $.validator.methods[ name ] = method; 2864 $.validator.messages[ name ] = message !== undefined ? message : $.validator.messages[ name ]; 2865 if ( method.length < 3 ) { 2866 $.validator.addClassRules( name, $.validator.normalizeRule( name ) ); 2867 } 2868 }, 2869 2870 methods: { 2871 2872 // http://jqueryvalidation.org/required-method/ 2873 required: function( value, element, param ) { 2874 // check if dependency is met 2875 if ( !this.depend( param, element ) ) { 2876 return "dependency-mismatch"; 2877 } 2878 if ( element.nodeName.toLowerCase() === "select" ) { 2879 // could be an array for select-multiple or a string, both are fine this way 2880 var val = $( element ).val(); 2881 return val && val.length > 0; 2882 } 2883 if ( this.checkable( element ) ) { 2884 return this.getLength( value, element ) > 0; 2885 } 2886 return $.trim( value ).length > 0; 2887 }, 2888 2889 // http://jqueryvalidation.org/email-method/ 2890 email: function( value, element ) { 2891 // From http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#e-mail-state-%28type=email%29 2892 // Retrieved 2014-01-14 2893 // If you have a problem with this implementation, report a bug against the above spec 2894 // Or use custom methods to implement your own email validation 2895 return this.optional( element ) || /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test( value ); 2896 }, 2897 2898 // http://jqueryvalidation.org/url-method/ 2899 url: function( value, element ) { 2900 // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ 2901 return this.optional( element ) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( value ); 2902 }, 2903 2904 // http://jqueryvalidation.org/date-method/ 2905 date: function( value, element ) { 2906 return this.optional( element ) || !/Invalid|NaN/.test( new Date( value ).toString() ); 2907 }, 2908 2909 // http://jqueryvalidation.org/dateISO-method/ 2910 dateISO: function( value, element ) { 2911 return this.optional( element ) || /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test( value ); 2912 }, 2913 2914 // http://jqueryvalidation.org/number-method/ 2915 number: function( value, element ) { 2916 return this.optional( element ) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value ); 2917 }, 2918 2919 // http://jqueryvalidation.org/digits-method/ 2920 digits: function( value, element ) { 2921 return this.optional( element ) || /^\d+$/.test( value ); 2922 }, 2923 2924 // http://jqueryvalidation.org/creditcard-method/ 2925 // based on http://en.wikipedia.org/wiki/Luhn/ 2926 creditcard: function( value, element ) { 2927 if ( this.optional( element ) ) { 2928 return "dependency-mismatch"; 2929 } 2930 // accept only spaces, digits and dashes 2931 if ( /[^0-9 \-]+/.test( value ) ) { 2932 return false; 2933 } 2934 var nCheck = 0, 2935 nDigit = 0, 2936 bEven = false, 2937 n, cDigit; 2938 2939 value = value.replace( /\D/g, "" ); 2940 2941 // Basing min and max length on 2942 // http://developer.ean.com/general_info/Valid_Credit_Card_Types 2943 if ( value.length < 13 || value.length > 19 ) { 2944 return false; 2945 } 2946 2947 for ( n = value.length - 1; n >= 0; n--) { 2948 cDigit = value.charAt( n ); 2949 nDigit = parseInt( cDigit, 10 );
2950 if ( bEven ) { 2951 if ( ( nDigit *= 2 ) > 9 ) { 2952 nDigit -= 9; 2953 } 2954 } 2955 nCheck += nDigit; 2956 bEven = !bEven; 2957 } 2958 2959 return ( nCheck % 10 ) === 0; 2960 }, 2961 2962 // http://jqueryvalidation.org/minlength-method/ 2963 minlength: function( value, element, param ) { 2964 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 2965 return this.optional( element ) || length >= param; 2966 }, 2967 2968 // http://jqueryvalidation.org/maxlength-method/ 2969 maxlength: function( value, element, param ) { 2970 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 2971 return this.optional( element ) || length <= param; 2972 }, 2973 2974 // http://jqueryvalidation.org/rangelength-method/ 2975 rangelength: function( value, element, param ) { 2976 var length = $.isArray( value ) ? value.length : this.getLength( value, element ); 2977 return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] ); 2978 }, 2979 2980 // http://jqueryvalidation.org/min-method/ 2981 min: function( value, element, param ) { 2982 return this.optional( element ) || value >= param; 2983 }, 2984 2985 // http://jqueryvalidation.org/max-method/ 2986 max: function( value, element, param ) { 2987 return this.optional( element ) || value <= param; 2988 }, 2989 2990 // http://jqueryvalidation.org/range-method/ 2991 range: function( value, element, param ) { 2992 return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] ); 2993 }, 2994 2995 // http://jqueryvalidation.org/equalTo-method/ 2996 equalTo: function( value, element, param ) {
2997 // bind to the blur event of the target in order to revalidate whenever the target field is updated 2998 // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead 2999 var target = $( param ); 3000 if ( this.settings.onfocusout ) { 3001 target.unbind( ".validate-equalTo" ).bind( "blur.validate-equalTo", function() { 3002 $( element ).valid(); 3003 }); 3004 } 3005 return value === target.val(); 3006 }, 3007 3008 // http://jqueryvalidation.org/remote-method/ 3009 remote: function( value, element, param ) { 3010 if ( this.optional( element ) ) { 3011 return "dependency-mismatch"; 3012 } 3013 3014 var previous = this.previousValue( element ), 3015 validator, data; 3016 3017 if (!this.settings.messages[ element.name ] ) { 3018 this.settings.messages[ element.name ] = {}; 3019 } 3020 previous.originalMessage = this.settings.messages[ element.name ].remote; 3021 this.settings.messages[ element.name ].remote = previous.message; 3022 3023 param = typeof param === "string" && { url: param } || param; 3024 3025 if ( previous.old === value ) { 3026 return previous.valid; 3027 } 3028 3029 previous.old = value; 3030 validator = this; 3031 this.startRequest( element ); 3032 data = {}; 3033 data[ element.name ] = value; 3034 $.ajax( $.extend( true, { 3035 url: param, 3036 mode: "abort", 3037 port: "validate" + element.name, 3038 dataType: "json", 3039 data: data, 3040 context: validator.currentForm, 3041 success: function( response ) { 3042 var valid = response === true || response === "true", 3043 errors, message, submitted; 3044 3045 validator.settings.messages[ element.name ].remote = previous.originalMessage; 3046 if ( valid ) { 3047 submitted = validator.formSubmitted; 3048 validator.prepareElement( element ); 3049 validator.formSubmitted = submitted; 3050 validator.successList.push( element ); 3051 delete validator.invalid[ element.name ]; 3052 validator.showErrors(); 3053 } else { 3054 errors = {}; 3055 message = response || validator.defaultMessage( element, "remote" ); 3056 errors[ element.name ] = previous.message = $.isFunction( message ) ? message( value ) : message; 3057 validator.invalid[ element.name ] = true; 3058 validator.showErrors( errors ); 3059 } 3060 previous.valid = valid; 3061 validator.stopRequest( element, valid ); 3062 } 3063 }, param ) ); 3064 return "pending"; 3065 } 3066 3067 } 3068 3069}); 3070 3071$.format = function deprecated() { 3072 throw "$.format has been deprecated. Please use $.validator.format instead."; 3073}; 3074 3075// ajax mode: abort 3076// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); 3077// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() 3078 3079var pendingRequests = {}, 3080 ajax; 3081// Use a prefilter if available (1.5+) 3082if ( $.ajaxPrefilter ) { 3083 $.ajaxPrefilter(function( settings, _, xhr ) { 3084 var port = settings.port; 3085 if ( settings.mode === "abort" ) { 3086 if ( pendingRequests[port] ) { 3087 pendingRequests[port].abort(); 3088 } 3089 pendingRequests[port] = xhr; 3090 } 3091 }); 3092} else { 3093 // Proxy ajax 3094 ajax = $.ajax; 3095 $.ajax = function( settings ) { 3096 var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, 3097 port = ( "port" in settings ? settings : $.ajaxSettings ).port; 3098 if ( mode === "abort" ) { 3099 if ( pendingRequests[port] ) { 3100 pendingRequests[port].abort(); 3101 } 3102 pendingRequests[port] = ajax.apply(this, arguments); 3103 return pendingRequests[port]; 3104 } 3105 return ajax.apply(this, arguments); 3106 }; 3107} 3108 3109// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation 3110// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target 3111 3112$.extend($.fn, { 3113 validateDelegate: function( delegate, type, handler ) { 3114 return this.bind(type, function( event ) { 3115 var target = $(event.target); 3116 if ( target.is(delegate) ) { 3117 return handler.apply(target, arguments); 3118 } 3119 }); 3120 } 3121}); 3122 3123})); 3124/*! 3125** Unobtrusive validation support library for jQuery and jQuery Validate 3126** Copyright (C) Microsoft Corporation. All rights reserved. 3127*/ 3128 3129/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ 3130/*global document: false, jQuery: false */ 3131 3132(function ($) { 3133 var $jQval = $.validator, 3134 adapters, 3135 data_validation = "unobtrusiveValidation"; 3136 3137 function setValidationValues(options, ruleName, value) { 3138 options.rules[ruleName] = value; 3139 if (options.message) { 3140 options.messages[ruleName] = options.message; 3141 } 3142 } 3143 3144 function splitAndTrim(value) { 3145 return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); 3146 } 3147 3148 function escapeAttributeValue(value) { 3149 // As mentioned on http://api.jquery.com/category/selectors/ 3150 return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); 3151 } 3152 3153 function getModelPrefix(fieldName) { 3154 return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); 3155 } 3156 3157 function appendModelPrefix(value, prefix) { 3158 if (value.indexOf("*.") === 0) { 3159 value = value.replace("*.", prefix); 3160 } 3161 return value; 3162 } 3163 3164 function onError(error, inputElement) { // 'this' is the form element 3165 var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), 3166 replaceAttrValue = container.attr("data-valmsg-replace"), 3167 replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; 3168 3169 container.removeClass("field-validation-valid").addClass("field-validation-error"); 3170 error.data("unobtrusiveContainer", container); 3171 3172 if (replace) { 3173 container.empty(); 3174 error.removeClass("input-validation-error").appendTo(container); 3175 } 3176 else { 3177 error.hide(); 3178 } 3179 } 3180 3181 function onErrors(event, validator) { // 'this' is the form element 3182 var container = $(this).find("[data-valmsg-summary=true]"), 3183 list = container.find("ul"); 3184 3185 if (list && list.length && validator.errorList.length) { 3186 list.empty();
vendor: 15,714 bytes, lines 3187-3530
3187 container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); 3188 3189 $.each(validator.errorList, function () { 3190 $("<li />").html(this.message).appendTo(list); 3191 }); 3192 } 3193 } 3194 3195 function onSuccess(error) { // 'this' is the form element 3196 var container = error.data("unobtrusiveContainer"), 3197 replaceAttrValue = container.attr("data-valmsg-replace"), 3198 replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; 3199 3200 if (container) { 3201 container.addClass("field-validation-valid").removeClass("field-validation-error"); 3202 error.removeData("unobtrusiveContainer"); 3203 3204 if (replace) { 3205 container.empty(); 3206 } 3207 } 3208 } 3209 3210 function onReset(event) { // 'this' is the form element 3211 var $form = $(this); 3212 $form.data("validator").resetForm(); 3213 $form.find(".validation-summary-errors") 3214 .addClass("validation-summary-valid") 3215 .removeClass("validation-summary-errors"); 3216 $form.find(".field-validation-error") 3217 .addClass("field-validation-valid") 3218 .removeClass("field-validation-error") 3219 .removeData("unobtrusiveContainer") 3220 .find(">*") // If we were using valmsg-replace, get the underlying error 3221 .removeData("unobtrusiveContainer"); 3222 } 3223 3224 function validationInfo(form) { 3225 var $form = $(form), 3226 result = $form.data(data_validation), 3227 onResetProxy = $.proxy(onReset, form); 3228 3229 if (!result) { 3230 result = { 3231 options: { // options structure passed to jQuery Validate's validate() method 3232 errorClass: "input-validation-error", 3233 errorElement: "span", 3234 errorPlacement: $.proxy(onError, form), 3235 invalidHandler: $.proxy(onErrors, form), 3236 messages: {}, 3237 rules: {}, 3238 success: $.proxy(onSuccess, form) 3239 }, 3240 attachValidation: function () { 3241 $form 3242 .unbind("reset." + data_validation, onResetProxy) 3243 .bind("reset." + data_validation, onResetProxy) 3244 .validate(this.options); 3245 }, 3246 validate: function () { // a validation function that is called by unobtrusive Ajax 3247 $form.validate(); 3248 return $form.valid(); 3249 } 3250 }; 3251 $form.data(data_validation, result); 3252 } 3253 3254 return result; 3255 } 3256 3257 $jQval.unobtrusive = { 3258 adapters: [], 3259 3260 parseElement: function (element, skipAttach) { 3261 /// <summary> 3262 /// Parses a single HTML element for unobtrusive validation attributes. 3263 /// </summary> 3264 /// <param name="element" domElement="true">The HTML element to be parsed.</param> 3265 /// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the 3266 /// validation to the form. If parsing just this single element, you should specify true. 3267 /// If parsing several elements, you should specify false, and manually attach the validation 3268 /// to the form when you are finished. The default is false.</param> 3269 var $element = $(element), 3270 form = $element.parents("form")[0], 3271 valInfo, rules, messages; 3272 3273 if (!form) { // Cannot do client-side validation without a form 3274 return; 3275 } 3276 3277 valInfo = validationInfo(form); 3278 valInfo.options.rules[element.name] = rules = {}; 3279 valInfo.options.messages[element.name] = messages = {}; 3280 3281 $.each(this.adapters, function () { 3282 var prefix = "data-val-" + this.name, 3283 message = $element.attr(prefix), 3284 paramValues = {}; 3285 3286 if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) 3287 prefix += "-"; 3288 3289 $.each(this.params, function () { 3290 paramValues[this] = $element.attr(prefix + this); 3291 }); 3292 3293 this.adapt({ 3294 element: element, 3295 form: form, 3296 message: message, 3297 params: paramValues, 3298 rules: rules, 3299 messages: messages 3300 }); 3301 } 3302 }); 3303 3304 $.extend(rules, { "__dummy__": true }); 3305 3306 if (!skipAttach) { 3307 valInfo.attachValidation(); 3308 } 3309 }, 3310 3311 parse: function (selector) { 3312 /// <summary> 3313 /// Parses all the HTML elements in the specified selector. It looks for input elements decorated 3314 /// with the [data-val=true] attribute value and enables validation according to the data-val-* 3315 /// attribute values. 3316 /// </summary> 3317 /// <param name="selector" type="String">Any valid jQuery selector.</param> 3318 var $forms = $(selector) 3319 .parents("form") 3320 .andSelf() 3321 .add($(selector).find("form")) 3322 .filter("form"); 3323 3324 $(selector).find(":input[data-val=true]").each(function () { 3325 $jQval.unobtrusive.parseElement(this, true); 3326 }); 3327 3328 $forms.each(function () { 3329 var info = validationInfo(this); 3330 if (info) { 3331 info.attachValidation(); 3332 } 3333 }); 3334 } 3335 }; 3336 3337 adapters = $jQval.unobtrusive.adapters; 3338 3339 adapters.add = function (adapterName, params, fn) { 3340 /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary> 3341 /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used 3342 /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param> 3343 /// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will 3344 /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and 3345 /// mmmm is the parameter name).</param> 3346 /// <param name="fn" type="Function">The function to call, which adapts the values from the HTML 3347 /// attributes into jQuery Validate rules and/or messages.</param> 3348 /// <returns type="jQuery.validator.unobtrusive.adapters" /> 3349 if (!fn) { // Called with no params, just a function 3350 fn = params; 3351 params = []; 3352 } 3353 this.push({ name: adapterName, params: params, adapt: fn }); 3354 return this; 3355 }; 3356 3357 adapters.addBool = function (adapterName, ruleName) { 3358 /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 3359 /// the jQuery Validate validation rule has no parameter values.</summary> 3360 /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used 3361 /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param> 3362 /// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value 3363 /// of adapterName will be used instead.</param> 3364 /// <returns type="jQuery.validator.unobtrusive.adapters" /> 3365 return this.add(adapterName, function (options) { 3366 setValidationValues(options, ruleName || adapterName, true); 3367 }); 3368 }; 3369 3370 adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { 3371 /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 3372 /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and 3373 /// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary> 3374 /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used 3375 /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param> 3376 /// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only 3377 /// have a minimum value.</param> 3378 /// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only 3379 /// have a maximum value.</param> 3380 /// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you 3381 /// have both a minimum and maximum value.</param> 3382 /// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that 3383 /// contains the minimum value. The default is "min".</param> 3384 /// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that 3385 /// contains the maximum value. The default is "max".</param> 3386 /// <returns type="jQuery.validator.unobtrusive.adapters" /> 3387 return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { 3388 var min = options.params.min, 3389 max = options.params.max; 3390 3391 if (min && max) { 3392 setValidationValues(options, minMaxRuleName, [min, max]); 3393 } 3394 else if (min) { 3395 setValidationValues(options, minRuleName, min); 3396 } 3397 else if (max) { 3398 setValidationValues(options, maxRuleName, max); 3399 } 3400 }); 3401 }; 3402 3403 adapters.addSingleVal = function (adapterName, attribute, ruleName) { 3404 /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where 3405 /// the jQuery Validate validation rule has a single value.</summary> 3406 /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used 3407 /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param> 3408 /// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value. 3409 /// The default is "val".</param> 3410 /// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value 3411 /// of adapterName will be used instead.</param> 3412 /// <returns type="jQuery.validator.unobtrusive.adapters" /> 3413 return this.add(adapterName, [attribute || "val"], function (options) { 3414 setValidationValues(options, ruleName || adapterName, options.params[attribute]); 3415 }); 3416 }; 3417 3418 $jQval.addMethod("__dummy__", function (value, element, params) { 3419 return true; 3420 }); 3421 3422 $jQval.addMethod("regex", function (value, element, params) { 3423 var match; 3424 if (this.optional(element)) { 3425 return true; 3426 } 3427 3428 match = new RegExp(params).exec(value); 3429 return (match && (match.index === 0) && (match[0].length === value.length)); 3430 }); 3431 3432 $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { 3433 var match; 3434 if (nonalphamin) { 3435 match = value.match(/\W/g); 3436 match = match && match.length >= nonalphamin; 3437 } 3438 return match; 3439 }); 3440 3441 adapters.addSingleVal("accept", "exts").addSingleVal("regex", "pattern"); 3442 adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); 3443 adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); 3444 adapters.add("equalto", ["other"], function (options) { 3445 var prefix = getModelPrefix(options.element.name), 3446 other = options.params.other, 3447 fullOtherName = appendModelPrefix(other, prefix), 3448 element = $(options.form).find(":input[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; 3449 3450 setValidationValues(options, "equalTo", element); 3451 }); 3452 adapters.add("required", function (options) { 3453 // jQuery Validate equates "required" with "mandatory" for checkbox elements 3454 if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { 3455 setValidationValues(options, "required", true); 3456 } 3457 }); 3458 adapters.add("remote", ["url", "type", "additionalfields"], function (options) { 3459 var value = { 3460 url: options.params.url, 3461 type: options.params.type || "GET", 3462 data: {} 3463 }, 3464 prefix = getModelPrefix(options.element.name); 3465 3466 $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { 3467 var paramName = appendModelPrefix(fieldName, prefix); 3468 value.data[paramName] = function () { 3469 return $(options.form).find(":input[name='" + escapeAttributeValue(paramName) + "']").val(); 3470 }; 3471 }); 3472 3473 setValidationValues(options, "remote", value); 3474 }); 3475 adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { 3476 if (options.params.min) { 3477 setValidationValues(options, "minlength", options.params.min); 3478 } 3479 if (options.params.nonalphamin) { 3480 setValidationValues(options, "nonalphamin", options.params.nonalphamin); 3481 } 3482 if (options.params.regex) { 3483 setValidationValues(options, "regex", options.params.regex); 3484 } 3485 }); 3486 3487 $(function () { 3488 $jQval.unobtrusive.parse(document); 3489 }); 3490} (jQuery)); 3491// Underscore.js 1.8.3 3492// http://underscorejs.org 3493// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 3494// Underscore may be freely distributed under the MIT license. 3495 3496(function() { 3497 3498 // Baseline setup 3499 // -------------- 3500 3501 // Establish the root object, `window` in the browser, or `exports` on the server. 3502 var root = this; 3503 3504 // Save the previous value of the `_` variable. 3505 var previousUnderscore = root._; 3506 3507 // Save bytes in the minified (but not gzipped) version: 3508 var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 3509 3510 // Create quick reference variables for speed access to core prototypes. 3511 var 3512 push = ArrayProto.push, 3513 slice = ArrayProto.slice, 3514 toString = ObjProto.toString, 3515 hasOwnProperty = ObjProto.hasOwnProperty; 3516 3517 // All **ECMAScript 5** native function implementations that we hope to use 3518 // are declared here. 3519 var 3520 nativeIsArray = Array.isArray, 3521 nativeKeys = Object.keys, 3522 nativeBind = FuncProto.bind, 3523 nativeCreate = Object.create; 3524 3525 // Naked function reference for surrogate-prototype-swapping. 3526 var Ctor = function(){}; 3527 3528 // Create a safe reference to the Underscore object for use below. 3529 var _ = function(obj) { 3530 if (obj instanceof _) return obj;
vendor: 53,040 bytes, lines 3531-5036
3531 if (!(this instanceof _)) return new _(obj); 3532 this._wrapped = obj; 3533 }; 3534 3535 // Export the Underscore object for **Node.js**, with 3536 // backwards-compatibility for the old `require()` API. If we're in 3537 // the browser, add `_` as a global object. 3538 if (typeof exports !== 'undefined') { 3539 if (typeof module !== 'undefined' && module.exports) { 3540 exports = module.exports = _; 3541 } 3542 exports._ = _; 3543 } else { 3544 root._ = _; 3545 } 3546 3547 // Current version. 3548 _.VERSION = '1.8.3'; 3549 3550 // Internal function that returns an efficient (for current engines) version 3551 // of the passed-in callback, to be repeatedly applied in other Underscore 3552 // functions. 3553 var optimizeCb = function(func, context, argCount) { 3554 if (context === void 0) return func; 3555 switch (argCount == null ? 3 : argCount) { 3556 case 1: return function(value) { 3557 return func.call(context, value); 3558 }; 3559 case 2: return function(value, other) { 3560 return func.call(context, value, other); 3561 }; 3562 case 3: return function(value, index, collection) { 3563 return func.call(context, value, index, collection); 3564 }; 3565 case 4: return function(accumulator, value, index, collection) { 3566 return func.call(context, accumulator, value, index, collection); 3567 }; 3568 } 3569 return function() { 3570 return func.apply(context, arguments); 3571 }; 3572 }; 3573 3574 // A mostly-internal function to generate callbacks that can be applied 3575 // to each element in a collection, returning the desired result â either 3576 // identity, an arbitrary callback, a property matcher, or a property accessor. 3577 var cb = function(value, context, argCount) { 3578 if (value == null) return _.identity; 3579 if (_.isFunction(value)) return optimizeCb(value, context, argCount); 3580 if (_.isObject(value)) return _.matcher(value); 3581 return _.property(value); 3582 }; 3583 _.iteratee = function(value, context) { 3584 return cb(value, context, Infinity); 3585 }; 3586 3587 // An internal function for creating assigner functions. 3588 var createAssigner = function(keysFunc, undefinedOnly) { 3589 return function(obj) { 3590 var length = arguments.length; 3591 if (length < 2 || obj == null) return obj; 3592 for (var index = 1; index < length; index++) { 3593 var source = arguments[index], 3594 keys = keysFunc(source), 3595 l = keys.length; 3596 for (var i = 0; i < l; i++) { 3597 var key = keys[i]; 3598 if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; 3599 } 3600 } 3601 return obj; 3602 }; 3603 }; 3604 3605 // An internal function for creating a new object that inherits from another. 3606 var baseCreate = function(prototype) { 3607 if (!_.isObject(prototype)) return {}; 3608 if (nativeCreate) return nativeCreate(prototype); 3609 Ctor.prototype = prototype; 3610 var result = new Ctor; 3611 Ctor.prototype = null; 3612 return result; 3613 }; 3614 3615 var property = function(key) { 3616 return function(obj) { 3617 return obj == null ? void 0 : obj[key]; 3618 }; 3619 }; 3620 3621 // Helper for collection methods to determine whether a collection 3622 // should be iterated as an array or as an object 3623 // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength 3624 // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 3625 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; 3626 var getLength = property('length'); 3627 var isArrayLike = function(collection) { 3628 var length = getLength(collection); 3629 return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; 3630 }; 3631 3632 // Collection Functions 3633 // -------------------- 3634 3635 // The cornerstone, an `each` implementation, aka `forEach`. 3636 // Handles raw objects in addition to array-likes. Treats all 3637 // sparse array-likes as if they were dense. 3638 _.each = _.forEach = function(obj, iteratee, context) { 3639 iteratee = optimizeCb(iteratee, context); 3640 var i, length; 3641 if (isArrayLike(obj)) { 3642 for (i = 0, length = obj.length; i < length; i++) { 3643 iteratee(obj[i], i, obj); 3644 } 3645 } else { 3646 var keys = _.keys(obj); 3647 for (i = 0, length = keys.length; i < length; i++) { 3648 iteratee(obj[keys[i]], keys[i], obj); 3649 } 3650 } 3651 return obj; 3652 }; 3653 3654 // Return the results of applying the iteratee to each element. 3655 _.map = _.collect = function(obj, iteratee, context) { 3656 iteratee = cb(iteratee, context); 3657 var keys = !isArrayLike(obj) && _.keys(obj), 3658 length = (keys || obj).length, 3659 results = Array(length); 3660 for (var index = 0; index < length; index++) { 3661 var currentKey = keys ? keys[index] : index; 3662 results[index] = iteratee(obj[currentKey], currentKey, obj); 3663 } 3664 return results; 3665 }; 3666 3667 // Create a reducing function iterating left or right. 3668 function createReduce(dir) { 3669 // Optimized iterator function as using arguments.length 3670 // in the main function will deoptimize the, see #1991. 3671 function iterator(obj, iteratee, memo, keys, index, length) { 3672 for (; index >= 0 && index < length; index += dir) { 3673 var currentKey = keys ? keys[index] : index; 3674 memo = iteratee(memo, obj[currentKey], currentKey, obj); 3675 } 3676 return memo; 3677 } 3678 3679 return function(obj, iteratee, memo, context) { 3680 iteratee = optimizeCb(iteratee, context, 4); 3681 var keys = !isArrayLike(obj) && _.keys(obj), 3682 length = (keys || obj).length, 3683 index = dir > 0 ? 0 : length - 1; 3684 // Determine the initial value if none is provided. 3685 if (arguments.length < 3) { 3686 memo = obj[keys ? keys[index] : index]; 3687 index += dir; 3688 } 3689 return iterator(obj, iteratee, memo, keys, index, length); 3690 }; 3691 } 3692 3693 // **Reduce** builds up a single result from a list of values, aka `inject`, 3694 // or `foldl`. 3695 _.reduce = _.foldl = _.inject = createReduce(1); 3696 3697 // The right-associative version of reduce, also known as `foldr`. 3698 _.reduceRight = _.foldr = createReduce(-1); 3699 3700 // Return the first value which passes a truth test. Aliased as `detect`. 3701 _.find = _.detect = function(obj, predicate, context) { 3702 var key; 3703 if (isArrayLike(obj)) { 3704 key = _.findIndex(obj, predicate, context); 3705 } else { 3706 key = _.findKey(obj, predicate, context); 3707 } 3708 if (key !== void 0 && key !== -1) return obj[key]; 3709 }; 3710 3711 // Return all the elements that pass a truth test. 3712 // Aliased as `select`. 3713 _.filter = _.select = function(obj, predicate, context) { 3714 var results = []; 3715 predicate = cb(predicate, context); 3716 _.each(obj, function(value, index, list) { 3717 if (predicate(value, index, list)) results.push(value); 3718 }); 3719 return results; 3720 }; 3721 3722 // Return all the elements for which a truth test fails. 3723 _.reject = function(obj, predicate, context) { 3724 return _.filter(obj, _.negate(cb(predicate)), context); 3725 }; 3726 3727 // Determine whether all of the elements match a truth test. 3728 // Aliased as `all`. 3729 _.every = _.all = function(obj, predicate, context) { 3730 predicate = cb(predicate, context); 3731 var keys = !isArrayLike(obj) && _.keys(obj), 3732 length = (keys || obj).length; 3733 for (var index = 0; index < length; index++) { 3734 var currentKey = keys ? keys[index] : index; 3735 if (!predicate(obj[currentKey], currentKey, obj)) return false; 3736 } 3737 return true; 3738 }; 3739 3740 // Determine if at least one element in the object matches a truth test. 3741 // Aliased as `any`. 3742 _.some = _.any = function(obj, predicate, context) { 3743 predicate = cb(predicate, context); 3744 var keys = !isArrayLike(obj) && _.keys(obj), 3745 length = (keys || obj).length; 3746 for (var index = 0; index < length; index++) { 3747 var currentKey = keys ? keys[index] : index; 3748 if (predicate(obj[currentKey], currentKey, obj)) return true; 3749 } 3750 return false; 3751 }; 3752 3753 // Determine if the array or object contains a given item (using `===`). 3754 // Aliased as `includes` and `include`. 3755 _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { 3756 if (!isArrayLike(obj)) obj = _.values(obj); 3757 if (typeof fromIndex != 'number' || guard) fromIndex = 0; 3758 return _.indexOf(obj, item, fromIndex) >= 0; 3759 }; 3760 3761 // Invoke a method (with arguments) on every item in a collection. 3762 _.invoke = function(obj, method) { 3763 var args = slice.call(arguments, 2); 3764 var isFunc = _.isFunction(method); 3765 return _.map(obj, function(value) { 3766 var func = isFunc ? method : value[method]; 3767 return func == null ? func : func.apply(value, args); 3768 }); 3769 }; 3770 3771 // Convenience version of a common use case of `map`: fetching a property. 3772 _.pluck = function(obj, key) { 3773 return _.map(obj, _.property(key)); 3774 }; 3775 3776 // Convenience version of a common use case of `filter`: selecting only objects 3777 // containing specific `key:value` pairs. 3778 _.where = function(obj, attrs) { 3779 return _.filter(obj, _.matcher(attrs)); 3780 }; 3781 3782 // Convenience version of a common use case of `find`: getting the first object 3783 // containing specific `key:value` pairs. 3784 _.findWhere = function(obj, attrs) { 3785 return _.find(obj, _.matcher(attrs)); 3786 }; 3787 3788 // Return the maximum element (or element-based computation). 3789 _.max = function(obj, iteratee, context) { 3790 var result = -Infinity, lastComputed = -Infinity, 3791 value, computed; 3792 if (iteratee == null && obj != null) { 3793 obj = isArrayLike(obj) ? obj : _.values(obj); 3794 for (var i = 0, length = obj.length; i < length; i++) { 3795 value = obj[i]; 3796 if (value > result) { 3797 result = value; 3798 } 3799 } 3800 } else { 3801 iteratee = cb(iteratee, context); 3802 _.each(obj, function(value, index, list) { 3803 computed = iteratee(value, index, list); 3804 if (computed > lastComputed || computed === -Infinity && result === -Infinity) { 3805 result = value; 3806 lastComputed = computed; 3807 } 3808 }); 3809 } 3810 return result; 3811 }; 3812 3813 // Return the minimum element (or element-based computation). 3814 _.min = function(obj, iteratee, context) { 3815 var result = Infinity, lastComputed = Infinity, 3816 value, computed; 3817 if (iteratee == null && obj != null) { 3818 obj = isArrayLike(obj) ? obj : _.values(obj); 3819 for (var i = 0, length = obj.length; i < length; i++) { 3820 value = obj[i]; 3821 if (value < result) { 3822 result = value; 3823 } 3824 } 3825 } else { 3826 iteratee = cb(iteratee, context); 3827 _.each(obj, function(value, index, list) { 3828 computed = iteratee(value, index, list); 3829 if (computed < lastComputed || computed === Infinity && result === Infinity) { 3830 result = value; 3831 lastComputed = computed; 3832 } 3833 }); 3834 } 3835 return result; 3836 }; 3837 3838 // Shuffle a collection, using the modern version of the 3839 // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherâYates_shuffle). 3840 _.shuffle = function(obj) { 3841 var set = isArrayLike(obj) ? obj : _.values(obj); 3842 var length = set.length; 3843 var shuffled = Array(length); 3844 for (var index = 0, rand; index < length; index++) { 3845 rand = _.random(0, index); 3846 if (rand !== index) shuffled[index] = shuffled[rand]; 3847 shuffled[rand] = set[index]; 3848 } 3849 return shuffled; 3850 }; 3851 3852 // Sample **n** random values from a collection. 3853 // If **n** is not specified, returns a single random element. 3854 // The internal `guard` argument allows it to work with `map`. 3855 _.sample = function(obj, n, guard) { 3856 if (n == null || guard) { 3857 if (!isArrayLike(obj)) obj = _.values(obj); 3858 return obj[_.random(obj.length - 1)]; 3859 } 3860 return _.shuffle(obj).slice(0, Math.max(0, n)); 3861 }; 3862 3863 // Sort the object's values by a criterion produced by an iteratee. 3864 _.sortBy = function(obj, iteratee, context) { 3865 iteratee = cb(iteratee, context); 3866 return _.pluck(_.map(obj, function(value, index, list) { 3867 return { 3868 value: value, 3869 index: index, 3870 criteria: iteratee(value, index, list) 3871 }; 3872 }).sort(function(left, right) { 3873 var a = left.criteria; 3874 var b = right.criteria; 3875 if (a !== b) { 3876 if (a > b || a === void 0) return 1; 3877 if (a < b || b === void 0) return -1; 3878 } 3879 return left.index - right.index; 3880 }), 'value'); 3881 }; 3882 3883 // An internal function used for aggregate "group by" operations. 3884 var group = function(behavior) { 3885 return function(obj, iteratee, context) { 3886 var result = {}; 3887 iteratee = cb(iteratee, context); 3888 _.each(obj, function(value, index) { 3889 var key = iteratee(value, index, obj); 3890 behavior(result, value, key); 3891 }); 3892 return result; 3893 }; 3894 }; 3895 3896 // Groups the object's values by a criterion. Pass either a string attribute 3897 // to group by, or a function that returns the criterion. 3898 _.groupBy = group(function(result, value, key) { 3899 if (_.has(result, key)) result[key].push(value); else result[key] = [value]; 3900 }); 3901 3902 // Indexes the object's values by a criterion, similar to `groupBy`, but for 3903 // when you know that your index values will be unique. 3904 _.indexBy = group(function(result, value, key) { 3905 result[key] = value; 3906 }); 3907 3908 // Counts instances of an object that group by a certain criterion. Pass 3909 // either a string attribute to count by, or a function that returns the 3910 // criterion. 3911 _.countBy = group(function(result, value, key) { 3912 if (_.has(result, key)) result[key]++; else result[key] = 1; 3913 }); 3914 3915 // Safely create a real, live array from anything iterable. 3916 _.toArray = function(obj) { 3917 if (!obj) return []; 3918 if (_.isArray(obj)) return slice.call(obj); 3919 if (isArrayLike(obj)) return _.map(obj, _.identity); 3920 return _.values(obj); 3921 }; 3922 3923 // Return the number of elements in an object. 3924 _.size = function(obj) { 3925 if (obj == null) return 0; 3926 return isArrayLike(obj) ? obj.length : _.keys(obj).length; 3927 }; 3928 3929 // Split a collection into two arrays: one whose elements all satisfy the given 3930 // predicate, and one whose elements all do not satisfy the predicate. 3931 _.partition = function(obj, predicate, context) { 3932 predicate = cb(predicate, context); 3933 var pass = [], fail = []; 3934 _.each(obj, function(value, key, obj) { 3935 (predicate(value, key, obj) ? pass : fail).push(value); 3936 }); 3937 return [pass, fail]; 3938 }; 3939 3940 // Array Functions 3941 // --------------- 3942 3943 // Get the first element of an array. Passing **n** will return the first N 3944 // values in the array. Aliased as `head` and `take`. The **guard** check 3945 // allows it to work with `_.map`. 3946 _.first = _.head = _.take = function(array, n, guard) { 3947 if (array == null) return void 0; 3948 if (n == null || guard) return array[0]; 3949 return _.initial(array, array.length - n); 3950 }; 3951 3952 // Returns everything but the last entry of the array. Especially useful on 3953 // the arguments object. Passing **n** will return all the values in 3954 // the array, excluding the last N. 3955 _.initial = function(array, n, guard) { 3956 return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); 3957 }; 3958 3959 // Get the last element of an array. Passing **n** will return the last N 3960 // values in the array. 3961 _.last = function(array, n, guard) { 3962 if (array == null) return void 0; 3963 if (n == null || guard) return array[array.length - 1]; 3964 return _.rest(array, Math.max(0, array.length - n)); 3965 }; 3966 3967 // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 3968 // Especially useful on the arguments object. Passing an **n** will return 3969 // the rest N values in the array. 3970 _.rest = _.tail = _.drop = function(array, n, guard) { 3971 return slice.call(array, n == null || guard ? 1 : n); 3972 }; 3973 3974 // Trim out all falsy values from an array. 3975 _.compact = function(array) { 3976 return _.filter(array, _.identity); 3977 }; 3978 3979 // Internal implementation of a recursive `flatten` function. 3980 var flatten = function(input, shallow, strict, startIndex) { 3981 var output = [], idx = 0; 3982 for (var i = startIndex || 0, length = getLength(input); i < length; i++) { 3983 var value = input[i]; 3984 if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { 3985 //flatten current level of array or arguments object 3986 if (!shallow) value = flatten(value, shallow, strict); 3987 var j = 0, len = value.length; 3988 output.length += len; 3989 while (j < len) { 3990 output[idx++] = value[j++]; 3991 } 3992 } else if (!strict) { 3993 output[idx++] = value; 3994 } 3995 } 3996 return output; 3997 }; 3998 3999 // Flatten out an array, either recursively (by default), or just one level. 4000 _.flatten = function(array, shallow) { 4001 return flatten(array, shallow, false); 4002 }; 4003 4004 // Return a version of the array that does not contain the specified value(s). 4005 _.without = function(array) { 4006 return _.difference(array, slice.call(arguments, 1)); 4007 }; 4008 4009 // Produce a duplicate-free version of the array. If the array has already 4010 // been sorted, you have the option of using a faster algorithm. 4011 // Aliased as `unique`. 4012 _.uniq = _.unique = function(array, isSorted, iteratee, context) { 4013 if (!_.isBoolean(isSorted)) { 4014 context = iteratee; 4015 iteratee = isSorted; 4016 isSorted = false; 4017 } 4018 if (iteratee != null) iteratee = cb(iteratee, context); 4019 var result = []; 4020 var seen = []; 4021 for (var i = 0, length = getLength(array); i < length; i++) { 4022 var value = array[i], 4023 computed = iteratee ? iteratee(value, i, array) : value; 4024 if (isSorted) { 4025 if (!i || seen !== computed) result.push(value); 4026 seen = computed; 4027 } else if (iteratee) { 4028 if (!_.contains(seen, computed)) { 4029 seen.push(computed); 4030 result.push(value); 4031 } 4032 } else if (!_.contains(result, value)) { 4033 result.push(value); 4034 } 4035 } 4036 return result; 4037 }; 4038 4039 // Produce an array that contains the union: each distinct element from all of 4040 // the passed-in arrays. 4041 _.union = function() { 4042 return _.uniq(flatten(arguments, true, true)); 4043 }; 4044 4045 // Produce an array that contains every item shared between all the 4046 // passed-in arrays. 4047 _.intersection = function(array) { 4048 var result = []; 4049 var argsLength = arguments.length; 4050 for (var i = 0, length = getLength(array); i < length; i++) { 4051 var item = array[i]; 4052 if (_.contains(result, item)) continue; 4053 for (var j = 1; j < argsLength; j++) { 4054 if (!_.contains(arguments[j], item)) break; 4055 } 4056 if (j === argsLength) result.push(item); 4057 } 4058 return result; 4059 }; 4060 4061 // Take the difference between one array and a number of other arrays. 4062 // Only the elements present in just the first array will remain. 4063 _.difference = function(array) { 4064 var rest = flatten(arguments, true, true, 1); 4065 return _.filter(array, function(value){ 4066 return !_.contains(rest, value); 4067 }); 4068 }; 4069 4070 // Zip together multiple lists into a single array -- elements that share 4071 // an index go together. 4072 _.zip = function() { 4073 return _.unzip(arguments); 4074 }; 4075 4076 // Complement of _.zip. Unzip accepts an array of arrays and groups 4077 // each array's elements on shared indices 4078 _.unzip = function(array) { 4079 var length = array && _.max(array, getLength).length || 0; 4080 var result = Array(length); 4081 4082 for (var index = 0; index < length; index++) { 4083 result[index] = _.pluck(array, index); 4084 } 4085 return result; 4086 }; 4087 4088 // Converts lists into objects. Pass either a single array of `[key, value]` 4089 // pairs, or two parallel arrays of the same length -- one of keys, and one of 4090 // the corresponding values. 4091 _.object = function(list, values) { 4092 var result = {}; 4093 for (var i = 0, length = getLength(list); i < length; i++) { 4094 if (values) { 4095 result[list[i]] = values[i]; 4096 } else { 4097 result[list[i][0]] = list[i][1]; 4098 } 4099 } 4100 return result; 4101 }; 4102 4103 // Generator function to create the findIndex and findLastIndex functions 4104 function createPredicateIndexFinder(dir) { 4105 return function(array, predicate, context) { 4106 predicate = cb(predicate, context); 4107 var length = getLength(array); 4108 var index = dir > 0 ? 0 : length - 1; 4109 for (; index >= 0 && index < length; index += dir) { 4110 if (predicate(array[index], index, array)) return index; 4111 } 4112 return -1; 4113 }; 4114 } 4115 4116 // Returns the first index on an array-like that passes a predicate test 4117 _.findIndex = createPredicateIndexFinder(1); 4118 _.findLastIndex = createPredicateIndexFinder(-1); 4119 4120 // Use a comparator function to figure out the smallest index at which 4121 // an object should be inserted so as to maintain order. Uses binary search. 4122 _.sortedIndex = function(array, obj, iteratee, context) { 4123 iteratee = cb(iteratee, context, 1); 4124 var value = iteratee(obj); 4125 var low = 0, high = getLength(array); 4126 while (low < high) { 4127 var mid = Math.floor((low + high) / 2); 4128 if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; 4129 } 4130 return low; 4131 }; 4132 4133 // Generator function to create the indexOf and lastIndexOf functions 4134 function createIndexFinder(dir, predicateFind, sortedIndex) { 4135 return function(array, item, idx) { 4136 var i = 0, length = getLength(array); 4137 if (typeof idx == 'number') { 4138 if (dir > 0) { 4139 i = idx >= 0 ? idx : Math.max(idx + length, i); 4140 } else { 4141 length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; 4142 } 4143 } else if (sortedIndex && idx && length) { 4144 idx = sortedIndex(array, item); 4145 return array[idx] === item ? idx : -1; 4146 } 4147 if (item !== item) { 4148 idx = predicateFind(slice.call(array, i, length), _.isNaN); 4149 return idx >= 0 ? idx + i : -1; 4150 } 4151 for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { 4152 if (array[idx] === item) return idx; 4153 } 4154 return -1; 4155 }; 4156 } 4157 4158 // Return the position of the first occurrence of an item in an array, 4159 // or -1 if the item is not included in the array. 4160 // If the array is large and already in sort order, pass `true` 4161 // for **isSorted** to use binary search. 4162 _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); 4163 _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); 4164 4165 // Generate an integer Array containing an arithmetic progression. A port of 4166 // the native Python `range()` function. See 4167 // [the Python documentation](http://docs.python.org/library/functions.html#range). 4168 _.range = function(start, stop, step) { 4169 if (stop == null) { 4170 stop = start || 0; 4171 start = 0; 4172 } 4173 step = step || 1; 4174 4175 var length = Math.max(Math.ceil((stop - start) / step), 0); 4176 var range = Array(length); 4177 4178 for (var idx = 0; idx < length; idx++, start += step) { 4179 range[idx] = start; 4180 } 4181 4182 return range; 4183 }; 4184 4185 // Function (ahem) Functions 4186 // ------------------ 4187 4188 // Determines whether to execute a function as a constructor 4189 // or a normal function with the provided arguments 4190 var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { 4191 if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); 4192 var self = baseCreate(sourceFunc.prototype); 4193 var result = sourceFunc.apply(self, args); 4194 if (_.isObject(result)) return result; 4195 return self; 4196 }; 4197 4198 // Create a function bound to a given object (assigning `this`, and arguments, 4199 // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 4200 // available. 4201 _.bind = function(func, context) { 4202 if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 4203 if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); 4204 var args = slice.call(arguments, 2); 4205 var bound = function() { 4206 return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); 4207 }; 4208 return bound; 4209 }; 4210 4211 // Partially apply a function by creating a version that has had some of its 4212 // arguments pre-filled, without changing its dynamic `this` context. _ acts 4213 // as a placeholder, allowing any combination of arguments to be pre-filled. 4214 _.partial = function(func) { 4215 var boundArgs = slice.call(arguments, 1); 4216 var bound = function() { 4217 var position = 0, length = boundArgs.length; 4218 var args = Array(length); 4219 for (var i = 0; i < length; i++) { 4220 args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i]; 4221 } 4222 while (position < arguments.length) args.push(arguments[position++]); 4223 return executeBound(func, bound, this, this, args); 4224 }; 4225 return bound; 4226 }; 4227 4228 // Bind a number of an object's methods to that object. Remaining arguments 4229 // are the method names to be bound. Useful for ensuring that all callbacks 4230 // defined on an object belong to it. 4231 _.bindAll = function(obj) { 4232 var i, length = arguments.length, key; 4233 if (length <= 1) throw new Error('bindAll must be passed function names'); 4234 for (i = 1; i < length; i++) { 4235 key = arguments[i]; 4236 obj[key] = _.bind(obj[key], obj); 4237 } 4238 return obj; 4239 }; 4240 4241 // Memoize an expensive function by storing its results. 4242 _.memoize = function(func, hasher) { 4243 var memoize = function(key) { 4244 var cache = memoize.cache; 4245 var address = '' + (hasher ? hasher.apply(this, arguments) : key); 4246 if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); 4247 return cache[address]; 4248 }; 4249 memoize.cache = {}; 4250 return memoize; 4251 }; 4252 4253 // Delays a function for the given number of milliseconds, and then calls 4254 // it with the arguments supplied. 4255 _.delay = function(func, wait) { 4256 var args = slice.call(arguments, 2); 4257 return setTimeout(function(){ 4258 return func.apply(null, args); 4259 }, wait); 4260 }; 4261 4262 // Defers a function, scheduling it to run after the current call stack has 4263 // cleared. 4264 _.defer = _.partial(_.delay, _, 1); 4265 4266 // Returns a function, that, when invoked, will only be triggered at most once 4267 // during a given window of time. Normally, the throttled function will run 4268 // as much as it can, without ever going more than once per `wait` duration; 4269 // but if you'd like to disable the execution on the leading edge, pass 4270 // `{leading: false}`. To disable execution on the trailing edge, ditto. 4271 _.throttle = function(func, wait, options) { 4272 var context, args, result; 4273 var timeout = null; 4274 var previous = 0; 4275 if (!options) options = {}; 4276 var later = function() { 4277 previous = options.leading === false ? 0 : _.now(); 4278 timeout = null; 4279 result = func.apply(context, args); 4280 if (!timeout) context = args = null; 4281 }; 4282 return function() { 4283 var now = _.now(); 4284 if (!previous && options.leading === false) previous = now; 4285 var remaining = wait - (now - previous); 4286 context = this; 4287 args = arguments; 4288 if (remaining <= 0 || remaining > wait) { 4289 if (timeout) { 4290 clearTimeout(timeout); 4291 timeout = null; 4292 } 4293 previous = now; 4294 result = func.apply(context, args); 4295 if (!timeout) context = args = null; 4296 } else if (!timeout && options.trailing !== false) { 4297 timeout = setTimeout(later, remaining); 4298 } 4299 return result; 4300 }; 4301 }; 4302 4303 // Returns a function, that, as long as it continues to be invoked, will not 4304 // be triggered. The function will be called after it stops being called for 4305 // N milliseconds. If `immediate` is passed, trigger the function on the 4306 // leading edge, instead of the trailing. 4307 _.debounce = function(func, wait, immediate) { 4308 var timeout, args, context, timestamp, result; 4309 4310 var later = function() { 4311 var last = _.now() - timestamp; 4312 4313 if (last < wait && last >= 0) { 4314 timeout = setTimeout(later, wait - last); 4315 } else { 4316 timeout = null; 4317 if (!immediate) { 4318 result = func.apply(context, args); 4319 if (!timeout) context = args = null; 4320 } 4321 } 4322 }; 4323 4324 return function() { 4325 context = this; 4326 args = arguments; 4327 timestamp = _.now(); 4328 var callNow = immediate && !timeout; 4329 if (!timeout) timeout = setTimeout(later, wait); 4330 if (callNow) { 4331 result = func.apply(context, args); 4332 context = args = null; 4333 } 4334 4335 return result; 4336 }; 4337 }; 4338 4339 // Returns the first function passed as an argument to the second, 4340 // allowing you to adjust arguments, run code before and after, and 4341 // conditionally execute the original function. 4342 _.wrap = function(func, wrapper) { 4343 return _.partial(wrapper, func); 4344 }; 4345 4346 // Returns a negated version of the passed-in predicate. 4347 _.negate = function(predicate) { 4348 return function() { 4349 return !predicate.apply(this, arguments); 4350 }; 4351 }; 4352 4353 // Returns a function that is the composition of a list of functions, each 4354 // consuming the return value of the function that follows. 4355 _.compose = function() { 4356 var args = arguments; 4357 var start = args.length - 1; 4358 return function() { 4359 var i = start; 4360 var result = args[start].apply(this, arguments); 4361 while (i--) result = args[i].call(this, result); 4362 return result; 4363 }; 4364 }; 4365 4366 // Returns a function that will only be executed on and after the Nth call. 4367 _.after = function(times, func) { 4368 return function() { 4369 if (--times < 1) { 4370 return func.apply(this, arguments); 4371 } 4372 }; 4373 }; 4374 4375 // Returns a function that will only be executed up to (but not including) the Nth call. 4376 _.before = function(times, func) { 4377 var memo; 4378 return function() { 4379 if (--times > 0) { 4380 memo = func.apply(this, arguments); 4381 } 4382 if (times <= 1) func = null; 4383 return memo; 4384 }; 4385 }; 4386 4387 // Returns a function that will be executed at most one time, no matter how 4388 // often you call it. Useful for lazy initialization. 4389 _.once = _.partial(_.before, 2); 4390 4391 // Object Functions 4392 // ---------------- 4393 4394 // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. 4395 var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); 4396 var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', 4397 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; 4398 4399 function collectNonEnumProps(obj, keys) { 4400 var nonEnumIdx = nonEnumerableProps.length; 4401 var constructor = obj.constructor; 4402 var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; 4403 4404 // Constructor is a special case. 4405 var prop = 'constructor'; 4406 if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop); 4407 4408 while (nonEnumIdx--) { 4409 prop = nonEnumerableProps[nonEnumIdx]; 4410 if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { 4411 keys.push(prop); 4412 } 4413 } 4414 } 4415 4416 // Retrieve the names of an object's own properties. 4417 // Delegates to **ECMAScript 5**'s native `Object.keys` 4418 _.keys = function(obj) { 4419 if (!_.isObject(obj)) return []; 4420 if (nativeKeys) return nativeKeys(obj); 4421 var keys = []; 4422 for (var key in obj) if (_.has(obj, key)) keys.push(key); 4423 // Ahem, IE < 9. 4424 if (hasEnumBug) collectNonEnumProps(obj, keys); 4425 return keys; 4426 }; 4427 4428 // Retrieve all the property names of an object. 4429 _.allKeys = function(obj) { 4430 if (!_.isObject(obj)) return []; 4431 var keys = []; 4432 for (var key in obj) keys.push(key); 4433 // Ahem, IE < 9. 4434 if (hasEnumBug) collectNonEnumProps(obj, keys); 4435 return keys; 4436 }; 4437 4438 // Retrieve the values of an object's properties. 4439 _.values = function(obj) { 4440 var keys = _.keys(obj); 4441 var length = keys.length; 4442 var values = Array(length); 4443 for (var i = 0; i < length; i++) { 4444 values[i] = obj[keys[i]]; 4445 } 4446 return values; 4447 }; 4448 4449 // Returns the results of applying the iteratee to each element of the object 4450 // In contrast to _.map it returns an object 4451 _.mapObject = function(obj, iteratee, context) { 4452 iteratee = cb(iteratee, context); 4453 var keys = _.keys(obj), 4454 length = keys.length, 4455 results = {}, 4456 currentKey; 4457 for (var index = 0; index < length; index++) { 4458 currentKey = keys[index]; 4459 results[currentKey] = iteratee(obj[currentKey], currentKey, obj); 4460 } 4461 return results; 4462 }; 4463 4464 // Convert an object into a list of `[key, value]` pairs. 4465 _.pairs = function(obj) { 4466 var keys = _.keys(obj); 4467 var length = keys.length; 4468 var pairs = Array(length); 4469 for (var i = 0; i < length; i++) { 4470 pairs[i] = [keys[i], obj[keys[i]]]; 4471 } 4472 return pairs; 4473 }; 4474 4475 // Invert the keys and values of an object. The values must be serializable. 4476 _.invert = function(obj) { 4477 var result = {}; 4478 var keys = _.keys(obj); 4479 for (var i = 0, length = keys.length; i < length; i++) { 4480 result[obj[keys[i]]] = keys[i]; 4481 } 4482 return result; 4483 }; 4484 4485 // Return a sorted list of the function names available on the object. 4486 // Aliased as `methods` 4487 _.functions = _.methods = function(obj) { 4488 var names = []; 4489 for (var key in obj) { 4490 if (_.isFunction(obj[key])) names.push(key); 4491 } 4492 return names.sort(); 4493 }; 4494 4495 // Extend a given object with all the properties in passed-in object(s). 4496 _.extend = createAssigner(_.allKeys); 4497 4498 // Assigns a given object with all the own properties in the passed-in object(s) 4499 // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 4500 _.extendOwn = _.assign = createAssigner(_.keys); 4501 4502 // Returns the first key on an object that passes a predicate test 4503 _.findKey = function(obj, predicate, context) { 4504 predicate = cb(predicate, context); 4505 var keys = _.keys(obj), key; 4506 for (var i = 0, length = keys.length; i < length; i++) { 4507 key = keys[i]; 4508 if (predicate(obj[key], key, obj)) return key; 4509 } 4510 }; 4511 4512 // Return a copy of the object only containing the whitelisted properties. 4513 _.pick = function(object, oiteratee, context) { 4514 var result = {}, obj = object, iteratee, keys; 4515 if (obj == null) return result; 4516 if (_.isFunction(oiteratee)) { 4517 keys = _.allKeys(obj); 4518 iteratee = optimizeCb(oiteratee, context); 4519 } else { 4520 keys = flatten(arguments, false, false, 1); 4521 iteratee = function(value, key, obj) { return key in obj; }; 4522 obj = Object(obj); 4523 } 4524 for (var i = 0, length = keys.length; i < length; i++) { 4525 var key = keys[i]; 4526 var value = obj[key]; 4527 if (iteratee(value, key, obj)) result[key] = value; 4528 } 4529 return result; 4530 }; 4531 4532 // Return a copy of the object without the blacklisted properties. 4533 _.omit = function(obj, iteratee, context) { 4534 if (_.isFunction(iteratee)) { 4535 iteratee = _.negate(iteratee); 4536 } else { 4537 var keys = _.map(flatten(arguments, false, false, 1), String); 4538 iteratee = function(value, key) { 4539 return !_.contains(keys, key); 4540 }; 4541 } 4542 return _.pick(obj, iteratee, context); 4543 }; 4544 4545 // Fill in a given object with default properties. 4546 _.defaults = createAssigner(_.allKeys, true); 4547 4548 // Creates an object that inherits from the given prototype object. 4549 // If additional properties are provided then they will be added to the 4550 // created object. 4551 _.create = function(prototype, props) { 4552 var result = baseCreate(prototype); 4553 if (props) _.extendOwn(result, props); 4554 return result; 4555 }; 4556 4557 // Create a (shallow-cloned) duplicate of an object. 4558 _.clone = function(obj) { 4559 if (!_.isObject(obj)) return obj; 4560 return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 4561 }; 4562 4563 // Invokes interceptor with the obj, and then returns obj. 4564 // The primary purpose of this method is to "tap into" a method chain, in 4565 // order to perform operations on intermediate results within the chain. 4566 _.tap = function(obj, interceptor) { 4567 interceptor(obj); 4568 return obj; 4569 }; 4570 4571 // Returns whether an object has a given set of `key:value` pairs. 4572 _.isMatch = function(object, attrs) { 4573 var keys = _.keys(attrs), length = keys.length; 4574 if (object == null) return !length; 4575 var obj = Object(object); 4576 for (var i = 0; i < length; i++) { 4577 var key = keys[i]; 4578 if (attrs[key] !== obj[key] || !(key in obj)) return false; 4579 } 4580 return true; 4581 }; 4582 4583 4584 // Internal recursive comparison function for `isEqual`. 4585 var eq = function(a, b, aStack, bStack) { 4586 // Identical objects are equal. `0 === -0`, but they aren't identical. 4587 // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). 4588 if (a === b) return a !== 0 || 1 / a === 1 / b; 4589 // A strict comparison is necessary because `null == undefined`. 4590 if (a == null || b == null) return a === b; 4591 // Unwrap any wrapped objects. 4592 if (a instanceof _) a = a._wrapped; 4593 if (b instanceof _) b = b._wrapped; 4594 // Compare `[[Class]]` names. 4595 var className = toString.call(a); 4596 if (className !== toString.call(b)) return false; 4597 switch (className) { 4598 // Strings, numbers, regular expressions, dates, and booleans are compared by value. 4599 case '[object RegExp]': 4600 // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') 4601 case '[object String]': 4602 // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 4603 // equivalent to `new String("5")`. 4604 return '' + a === '' + b; 4605 case '[object Number]': 4606 // `NaN`s are equivalent, but non-reflexive. 4607 // Object(NaN) is equivalent to NaN 4608 if (+a !== +a) return +b !== +b; 4609 // An `egal` comparison is performed for other numeric values. 4610 return +a === 0 ? 1 / +a === 1 / b : +a === +b; 4611 case '[object Date]': 4612 case '[object Boolean]': 4613 // Coerce dates and booleans to numeric primitive values. Dates are compared by their 4614 // millisecond representations. Note that invalid dates with millisecond representations 4615 // of `NaN` are not equivalent. 4616 return +a === +b; 4617 } 4618 4619 var areArrays = className === '[object Array]'; 4620 if (!areArrays) { 4621 if (typeof a != 'object' || typeof b != 'object') return false; 4622 4623 // Objects with different constructors are not equivalent, but `Object`s or `Array`s 4624 // from different frames are. 4625 var aCtor = a.constructor, bCtor = b.constructor; 4626 if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && 4627 _.isFunction(bCtor) && bCtor instanceof bCtor) 4628 && ('constructor' in a && 'constructor' in b)) { 4629 return false; 4630 } 4631 } 4632 // Assume equality for cyclic structures. The algorithm for detecting cyclic 4633 // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 4634 4635 // Initializing stack of traversed objects. 4636 // It's done here since we only need them for objects and arrays comparison. 4637 aStack = aStack || []; 4638 bStack = bStack || []; 4639 var length = aStack.length; 4640 while (length--) { 4641 // Linear search. Performance is inversely proportional to the number of 4642 // unique nested structures. 4643 if (aStack[length] === a) return bStack[length] === b; 4644 } 4645 4646 // Add the first object to the stack of traversed objects. 4647 aStack.push(a); 4648 bStack.push(b); 4649 4650 // Recursively compare objects and arrays. 4651 if (areArrays) { 4652 // Compare array lengths to determine if a deep comparison is necessary. 4653 length = a.length; 4654 if (length !== b.length) return false; 4655 // Deep compare the contents, ignoring non-numeric properties. 4656 while (length--) { 4657 if (!eq(a[length], b[length], aStack, bStack)) return false; 4658 } 4659 } else { 4660 // Deep compare objects. 4661 var keys = _.keys(a), key; 4662 length = keys.length; 4663 // Ensure that both objects contain the same number of properties before comparing deep equality. 4664 if (_.keys(b).length !== length) return false; 4665 while (length--) { 4666 // Deep compare each member 4667 key = keys[length]; 4668 if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false; 4669 } 4670 } 4671 // Remove the first object from the stack of traversed objects. 4672 aStack.pop(); 4673 bStack.pop(); 4674 return true; 4675 }; 4676 4677 // Perform a deep comparison to check if two objects are equal. 4678 _.isEqual = function(a, b) { 4679 return eq(a, b); 4680 }; 4681 4682 // Is a given array, string, or object empty? 4683 // An "empty" object has no enumerable own-properties. 4684 _.isEmpty = function(obj) { 4685 if (obj == null) return true; 4686 if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; 4687 return _.keys(obj).length === 0; 4688 }; 4689 4690 // Is a given value a DOM element? 4691 _.isElement = function(obj) { 4692 return !!(obj && obj.nodeType === 1); 4693 }; 4694 4695 // Is a given value an array? 4696 // Delegates to ECMA5's native Array.isArray 4697 _.isArray = nativeIsArray || function(obj) { 4698 return toString.call(obj) === '[object Array]'; 4699 }; 4700 4701 // Is a given variable an object? 4702 _.isObject = function(obj) { 4703 var type = typeof obj; 4704 return type === 'function' || type === 'object' && !!obj; 4705 }; 4706 4707 // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. 4708 _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { 4709 _['is' + name] = function(obj) { 4710 return toString.call(obj) === '[object ' + name + ']'; 4711 }; 4712 }); 4713 4714 // Define a fallback version of the method in browsers (ahem, IE < 9), where 4715 // there isn't any inspectable "Arguments" type. 4716 if (!_.isArguments(arguments)) { 4717 _.isArguments = function(obj) { 4718 return _.has(obj, 'callee'); 4719 }; 4720 } 4721 4722 // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, 4723 // IE 11 (#1621), and in Safari 8 (#1929). 4724 if (typeof /./ != 'function' && typeof Int8Array != 'object') { 4725 _.isFunction = function(obj) { 4726 return typeof obj == 'function' || false; 4727 }; 4728 } 4729 4730 // Is a given object a finite number? 4731 _.isFinite = function(obj) { 4732 return isFinite(obj) && !isNaN(parseFloat(obj)); 4733 }; 4734 4735 // Is the given value `NaN`? (NaN is the only number which does not equal itself). 4736 _.isNaN = function(obj) { 4737 return _.isNumber(obj) && obj !== +obj; 4738 }; 4739 4740 // Is a given value a boolean? 4741 _.isBoolean = function(obj) { 4742 return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; 4743 }; 4744 4745 // Is a given value equal to null? 4746 _.isNull = function(obj) { 4747 return obj === null; 4748 }; 4749 4750 // Is a given variable undefined? 4751 _.isUndefined = function(obj) { 4752 return obj === void 0; 4753 }; 4754 4755 // Shortcut function for checking if an object has a given property directly 4756 // on itself (in other words, not on a prototype). 4757 _.has = function(obj, key) { 4758 return obj != null && hasOwnProperty.call(obj, key); 4759 }; 4760 4761 // Utility Functions 4762 // ----------------- 4763 4764 // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 4765 // previous owner. Returns a reference to the Underscore object. 4766 _.noConflict = function() { 4767 root._ = previousUnderscore; 4768 return this; 4769 }; 4770 4771 // Keep the identity function around for default iteratees. 4772 _.identity = function(value) { 4773 return value; 4774 }; 4775 4776 // Predicate-generating functions. Often useful outside of Underscore. 4777 _.constant = function(value) { 4778 return function() { 4779 return value; 4780 }; 4781 }; 4782 4783 _.noop = function(){}; 4784 4785 _.property = property; 4786 4787 // Generates a function for a given object that returns a given property. 4788 _.propertyOf = function(obj) { 4789 return obj == null ? function(){} : function(key) { 4790 return obj[key]; 4791 }; 4792 }; 4793 4794 // Returns a predicate for checking whether an object has a given set of 4795 // `key:value` pairs. 4796 _.matcher = _.matches = function(attrs) { 4797 attrs = _.extendOwn({}, attrs); 4798 return function(obj) { 4799 return _.isMatch(obj, attrs); 4800 }; 4801 }; 4802 4803 // Run a function **n** times. 4804 _.times = function(n, iteratee, context) { 4805 var accum = Array(Math.max(0, n)); 4806 iteratee = optimizeCb(iteratee, context, 1); 4807 for (var i = 0; i < n; i++) accum[i] = iteratee(i); 4808 return accum; 4809 }; 4810 4811 // Return a random integer between min and max (inclusive). 4812 _.random = function(min, max) { 4813 if (max == null) { 4814 max = min; 4815 min = 0; 4816 } 4817 return min + Math.floor(Math.random() * (max - min + 1)); 4818 }; 4819 4820 // A (possibly faster) way to get the current timestamp as an integer. 4821 _.now = Date.now || function() { 4822 return new Date().getTime(); 4823 }; 4824 4825 // List of HTML entities for escaping. 4826 var escapeMap = { 4827 '&': '&', 4828 '<': '<', 4829 '>': '>', 4830 '"': '"', 4831 "'": ''', 4832 '`': '`' 4833 }; 4834 var unescapeMap = _.invert(escapeMap); 4835 4836 // Functions for escaping and unescaping strings to/from HTML interpolation. 4837 var createEscaper = function(map) { 4838 var escaper = function(match) { 4839 return map[match]; 4840 }; 4841 // Regexes for identifying a key that needs to be escaped 4842 var source = '(?:' + _.keys(map).join('|') + ')'; 4843 var testRegexp = RegExp(source); 4844 var replaceRegexp = RegExp(source, 'g'); 4845 return function(string) { 4846 string = string == null ? '' : '' + string; 4847 return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; 4848 }; 4849 }; 4850 _.escape = createEscaper(escapeMap); 4851 _.unescape = createEscaper(unescapeMap); 4852 4853 // If the value of the named `property` is a function then invoke it with the 4854 // `object` as context; otherwise, return it. 4855 _.result = function(object, property, fallback) { 4856 var value = object == null ? void 0 : object[property]; 4857 if (value === void 0) { 4858 value = fallback; 4859 } 4860 return _.isFunction(value) ? value.call(object) : value; 4861 }; 4862 4863 // Generate a unique integer id (unique within the entire client session). 4864 // Useful for temporary DOM ids. 4865 var idCounter = 0; 4866 _.uniqueId = function(prefix) { 4867 var id = ++idCounter + ''; 4868 return prefix ? prefix + id : id; 4869 }; 4870 4871 // By default, Underscore uses ERB-style template delimiters, change the 4872 // following template settings to use alternative delimiters. 4873 _.templateSettings = { 4874 evaluate : /<%([\s\S]+?)%>/g, 4875 interpolate : /<%=([\s\S]+?)%>/g, 4876 escape : /<%-([\s\S]+?)%>/g 4877 }; 4878 4879 // When customizing `templateSettings`, if you don't want to define an 4880 // interpolation, evaluation or escaping regex, we need one that is 4881 // guaranteed not to match. 4882 var noMatch = /(.)^/; 4883 4884 // Certain characters need to be escaped so that they can be put into a 4885 // string literal. 4886 var escapes = { 4887 "'": "'", 4888 '\\': '\\', 4889 '\r': 'r', 4890 '\n': 'n', 4891 '\u2028': 'u2028', 4892 '\u2029': 'u2029' 4893 }; 4894 4895 var escaper = /\\|'|\r|\n|\u2028|\u2029/g; 4896 4897 var escapeChar = function(match) { 4898 return '\\' + escapes[match]; 4899 }; 4900 4901 // JavaScript micro-templating, similar to John Resig's implementation. 4902 // Underscore templating handles arbitrary delimiters, preserves whitespace, 4903 // and correctly escapes quotes within interpolated code. 4904 // NB: `oldSettings` only exists for backwards compatibility. 4905 _.template = function(text, settings, oldSettings) { 4906 if (!settings && oldSettings) settings = oldSettings; 4907 settings = _.defaults({}, settings, _.templateSettings); 4908 4909 // Combine delimiters into one regular expression via alternation. 4910 var matcher = RegExp([ 4911 (settings.escape || noMatch).source, 4912 (settings.interpolate || noMatch).source, 4913 (settings.evaluate || noMatch).source 4914 ].join('|') + '|$', 'g'); 4915 4916 // Compile the template source, escaping string literals appropriately. 4917 var index = 0; 4918 var source = "__p+='"; 4919 text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 4920 source += text.slice(index, offset).replace(escaper, escapeChar); 4921 index = offset + match.length; 4922 4923 if (escape) { 4924 source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 4925 } else if (interpolate) { 4926 source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 4927 } else if (evaluate) { 4928 source += "';\n" + evaluate + "\n__p+='"; 4929 } 4930 4931 // Adobe VMs need the match returned to produce the correct offest. 4932 return match; 4933 }); 4934 source += "';\n"; 4935 4936 // If a variable is not specified, place data values in local scope. 4937 if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 4938 4939 source = "var __t,__p='',__j=Array.prototype.join," + 4940 "print=function(){__p+=__j.call(arguments,'');};\n" + 4941 source + 'return __p;\n'; 4942 4943 try { 4944 var render = new Function(settings.variable || 'obj', '_', source); 4945 } catch (e) { 4946 e.source = source; 4947 throw e; 4948 } 4949 4950 var template = function(data) { 4951 return render.call(this, data, _); 4952 }; 4953 4954 // Provide the compiled source as a convenience for precompilation. 4955 var argument = settings.variable || 'obj'; 4956 template.source = 'function(' + argument + '){\n' + source + '}'; 4957 4958 return template; 4959 }; 4960 4961 // Add a "chain" function. Start chaining a wrapped Underscore object. 4962 _.chain = function(obj) { 4963 var instance = _(obj); 4964 instance._chain = true; 4965 return instance; 4966 }; 4967 4968 // OOP 4969 // --------------- 4970 // If Underscore is called as a function, it returns a wrapped object that 4971 // can be used OO-style. This wrapper holds altered versions of all the 4972 // underscore functions. Wrapped objects may be chained. 4973 4974 // Helper function to continue chaining intermediate results. 4975 var result = function(instance, obj) { 4976 return instance._chain ? _(obj).chain() : obj; 4977 }; 4978 4979 // Add your own custom functions to the Underscore object. 4980 _.mixin = function(obj) { 4981 _.each(_.functions(obj), function(name) { 4982 var func = _[name] = obj[name]; 4983 _.prototype[name] = function() { 4984 var args = [this._wrapped]; 4985 push.apply(args, arguments); 4986 return result(this, func.apply(_, args)); 4987 }; 4988 }); 4989 }; 4990 4991 // Add all of the Underscore functions to the wrapper object. 4992 _.mixin(_); 4993 4994 // Add all mutator Array functions to the wrapper. 4995 _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 4996 var method = ArrayProto[name]; 4997 _.prototype[name] = function() { 4998 var obj = this._wrapped; 4999 method.apply(obj, arguments); 5000 if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; 5001 return result(this, obj); 5002 }; 5003 }); 5004 5005 // Add all accessor Array functions to the wrapper. 5006 _.each(['concat', 'join', 'slice'], function(name) { 5007 var method = ArrayProto[name]; 5008 _.prototype[name] = function() { 5009 return result(this, method.apply(this._wrapped, arguments)); 5010 }; 5011 }); 5012 5013 // Extracts the result from a wrapped and chained object. 5014 _.prototype.value = function() { 5015 return this._wrapped; 5016 }; 5017 5018 // Provide unwrapping proxy for some methods used in engine operations 5019 // such as arithmetic and JSON stringification. 5020 _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; 5021 5022 _.prototype.toString = function() { 5023 return '' + this._wrapped; 5024 }; 5025 5026 // AMD registration happens at the end for compatibility with AMD loaders 5027 // that may not enforce next-turn semantics on modules. Even though general 5028 // practice for AMD registration is to be anonymous, underscore registers 5029 // as a named module because, like jQuery, it is a base library that is 5030 // popular enough to be bundled in a third party lib, but not be part of 5031 // an AMD load request. Those cases could generate an error when an 5032 // anonymous define() is called outside of a loader request. 5033 if (typeof define === 'function' && define.amd) { 5034 define('underscore', [], function() { 5035 return _; 5036 });
5037 } 5038}.call(this)); 5039 5040/** 5041 * @license AngularJS v1.2.25 5042 * (c) 2010-2014 Google, Inc. http://angularjs.org 5043 * License: MIT 5044 */ 5045(function(window, document, undefined) {'use strict'; 5046 5047/** 5048 * @description 5049 * 5050 * This object provides a utility for producing rich Error messages within 5051 * Angular. It can be called as follows: 5052 * 5053 * var exampleMinErr = minErr('example'); 5054 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); 5055 * 5056 * The above creates an instance of minErr in the example namespace. The 5057 * resulting error will have a namespaced error code of example.one. The 5058 * resulting error will replace {0} with the value of foo, and {1} with the 5059 * value of bar. The object is not restricted in the number of arguments it can 5060 * take. 5061 * 5062 * If fewer arguments are specified than necessary for interpolation, the extra 5063 * interpolation markers will be preserved in the final string. 5064 * 5065 * Since data will be parsed statically during a build step, some restrictions 5066 * are applied with respect to how minErr instances are created and called. 5067 * Instances should have names of the form namespaceMinErr for a minErr created 5068 * using minErr('namespace') . Error codes, namespaces and template strings 5069 * should all be static strings, not variables or general expressions. 5070 * 5071 * @param {string} module The namespace to use for the new minErr instance. 5072 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance 5073 */ 5074 5075function minErr(module) { 5076 return function () { 5077 var code = arguments[0], 5078 prefix = '[' + (module ? module + ':' : '') + code + '] ', 5079 template = arguments[1], 5080 templateArgs = arguments, 5081 stringify = function (obj) { 5082 if (typeof obj === 'function') { 5083 return obj.toString().replace(/ \{[\s\S]*$/, ''); 5084 } else if (typeof obj === 'undefined') { 5085 return 'undefined'; 5086 } else if (typeof obj !== 'string') { 5087 return JSON.stringify(obj); 5088 } 5089 return obj; 5090 }, 5091 message, i; 5092 5093 message = prefix + template.replace(/\{\d+\}/g, function (match) { 5094 var index = +match.slice(1, -1), arg; 5095 5096 if (index + 2 < templateArgs.length) { 5097 arg = templateArgs[index + 2]; 5098 if (typeof arg === 'function') { 5099 return arg.toString().replace(/ ?\{[\s\S]*$/, ''); 5100 } else if (typeof arg === 'undefined') { 5101 return 'undefined'; 5102 } else if (typeof arg !== 'string') { 5103 return toJson(arg); 5104 } 5105 return arg; 5106 } 5107 return match; 5108 }); 5109 5110 message = message + '\nhttp://errors.angularjs.org/1.2.25/' + 5111 (module ? module + '/' : '') + code; 5112 for (i = 2; i < arguments.length; i++) { 5113 message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + 5114 encodeURIComponent(stringify(arguments[i])); 5115 } 5116 5117 return new Error(message); 5118 }; 5119} 5120 5121/* We need to tell jshint what variables are being exported */ 5122/* global angular: true, 5123 msie: true, 5124 jqLite: true, 5125 jQuery: true, 5126 slice: true, 5127 push: true, 5128 toString: true, 5129 ngMinErr: true, 5130 angularModule: true, 5131 nodeName_: true, 5132 uid: true, 5133 VALIDITY_STATE_PROPERTY: true, 5134 5135 lowercase: true, 5136 uppercase: true, 5137 manualLowercase: true, 5138 manualUppercase: true, 5139 nodeName_: true, 5140 isArrayLike: true, 5141 forEach: true, 5142 sortedKeys: true, 5143 forEachSorted: true, 5144 reverseParams: true, 5145 nextUid: true, 5146 setHashKey: true, 5147 extend: true, 5148 int: true, 5149 inherit: true, 5150 noop: true, 5151 identity: true, 5152 valueFn: true, 5153 isUndefined: true, 5154 isDefined: true, 5155 isObject: true, 5156 isString: true, 5157 isNumber: true, 5158 isDate: true, 5159 isArray: true, 5160 isFunction: true, 5161 isRegExp: true, 5162 isWindow: true, 5163 isScope: true, 5164 isFile: true, 5165 isBlob: true, 5166 isBoolean: true, 5167 isPromiseLike: true, 5168 trim: true, 5169 isElement: true, 5170 makeMap: true, 5171 map: true, 5172 size: true, 5173 includes: true, 5174 indexOf: true, 5175 arrayRemove: true, 5176 isLeafNode: true, 5177 copy: true, 5178 shallowCopy: true, 5179 equals: true, 5180 csp: true, 5181 concat: true, 5182 sliceArgs: true, 5183 bind: true, 5184 toJsonReplacer: true, 5185 toJson: true, 5186 fromJson: true,
5187 toBoolean: true, 5188 startingTag: true, 5189 tryDecodeURIComponent: true, 5190 parseKeyValue: true, 5191 toKeyValue: true, 5192 encodeUriSegment: true, 5193 encodeUriQuery: true, 5194 angularInit: true, 5195 bootstrap: true, 5196 snake_case: true, 5197 bindJQuery: true, 5198 assertArg: true, 5199 assertArgFn: true, 5200 assertNotHasOwnProperty: true, 5201 getter: true, 5202 getBlockElements: true, 5203 hasOwnProperty: true, 5204*/ 5205 5206//////////////////////////////////// 5207 5208/** 5209 * @ngdoc module 5210 * @name ng 5211 * @module ng 5212 * @description 5213 * 5214 * # ng (core module) 5215 * The ng module is loaded by default when an AngularJS application is started. The module itself 5216 * contains the essential components for an AngularJS application to function. The table below 5217 * lists a high level breakdown of each of the services/factories, filters, directives and testing 5218 * components available within this core module. 5219 * 5220 * <div doc-module-components="ng"></div> 5221 */ 5222 5223// The name of a form control's ValidityState property. 5224// This is used so that it's possible for internal tests to create mock ValidityStates. 5225var VALIDITY_STATE_PROPERTY = 'validity'; 5226 5227/** 5228 * @ngdoc function 5229 * @name angular.lowercase 5230 * @module ng 5231 * @kind function 5232 * 5233 * @description Converts the specified string to lowercase. 5234 * @param {string} string String to be converted to lowercase. 5235 * @returns {string} Lowercased string. 5236 */ 5237var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; 5238var hasOwnProperty = Object.prototype.hasOwnProperty; 5239 5240/** 5241 * @ngdoc function 5242 * @name angular.uppercase 5243 * @module ng 5244 * @kind function 5245 * 5246 * @description Converts the specified string to uppercase. 5247 * @param {string} string String to be converted to uppercase. 5248 * @returns {string} Uppercased string. 5249 */ 5250var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; 5251 5252 5253var manualLowercase = function(s) { 5254 /* jshint bitwise: false */ 5255 return isString(s) 5256 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) 5257 : s; 5258}; 5259var manualUppercase = function(s) { 5260 /* jshint bitwise: false */ 5261 return isString(s) 5262 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) 5263 : s; 5264}; 5265 5266 5267// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish 5268// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods 5269// with correct but slower alternatives. 5270if ('i' !== 'I'.toLowerCase()) { 5271 lowercase = manualLowercase; 5272 uppercase = manualUppercase; 5273} 5274 5275 5276var /** holds major version number for IE or NaN for real browsers */ 5277 msie, 5278 jqLite, // delay binding since jQuery could be loaded after us. 5279 jQuery, // delay binding 5280 slice = [].slice, 5281 push = [].push, 5282 toString = Object.prototype.toString, 5283 ngMinErr = minErr('ng'), 5284 5285 /** @name angular */ 5286 angular = window.angular || (window.angular = {}), 5287 angularModule, 5288 nodeName_, 5289 uid = ['0', '0', '0']; 5290 5291/** 5292 * IE 11 changed the format of the UserAgent string. 5293 * See http://msdn.microsoft.com/en-us/library/ms537503.aspx 5294 */ 5295msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); 5296if (isNaN(msie)) { 5297 msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); 5298} 5299 5300 5301/** 5302 * @private 5303 * @param {*} obj 5304 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, 5305 * String ...) 5306 */ 5307function isArrayLike(obj) { 5308 if (obj == null || isWindow(obj)) { 5309 return false; 5310 } 5311 5312 var length = obj.length; 5313 5314 if (obj.nodeType === 1 && length) { 5315 return true; 5316 } 5317 5318 return isString(obj) || isArray(obj) || length === 0 || 5319 typeof length === 'number' && length > 0 && (length - 1) in obj; 5320} 5321 5322/** 5323 * @ngdoc function 5324 * @name angular.forEach 5325 * @module ng 5326 * @kind function 5327 * 5328 * @description 5329 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an 5330 * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` 5331 * is the value of an object property or an array element and `key` is the object property key or 5332 * array element index. Specifying a `context` for the function is optional. 5333 * 5334 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters 5335 * using the `hasOwnProperty` method. 5336 * 5337 ```js 5338 var values = {name: 'misko', gender: 'male'}; 5339 var log = []; 5340 angular.forEach(values, function(value, key) { 5341 this.push(key + ': ' + value); 5342 }, log); 5343 expect(log).toEqual(['name: misko', 'gender: male']); 5344 ``` 5345 * 5346 * @param {Object|Array} obj Object to iterate over. 5347 * @param {Function} iterator Iterator function. 5348 * @param {Object=} context Object to become context (`this`) for the iterator function. 5349 * @returns {Object|Array} Reference to `obj`. 5350 */ 5351function forEach(obj, iterator, context) { 5352 var key; 5353 if (obj) { 5354 if (isFunction(obj)) { 5355 for (key in obj) { 5356 // Need to check if hasOwnProperty exists, 5357 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function 5358 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { 5359 iterator.call(context, obj[key], key); 5360 } 5361 } 5362 } else if (isArray(obj) || isArrayLike(obj)) { 5363 for (key = 0; key < obj.length; key++) { 5364 iterator.call(context, obj[key], key); 5365 } 5366 }
5366 else if (obj.forEach && obj.forEach !== forEach) { 5367 obj.forEach(iterator, context); 5368 } else { 5369 for (key in obj) { 5370 if (obj.hasOwnProperty(key)) { 5371 iterator.call(context, obj[key], key); 5372 } 5373 } 5374 } 5375 } 5376 return obj; 5377} 5378 5379function sortedKeys(obj) { 5380 var keys = []; 5381 for (var key in obj) { 5382 if (obj.hasOwnProperty(key)) { 5383 keys.push(key); 5384 } 5385 } 5386 return keys.sort(); 5387} 5388 5389function forEachSorted(obj, iterator, context) { 5390 var keys = sortedKeys(obj); 5391 for ( var i = 0; i < keys.length; i++) { 5392 iterator.call(context, obj[keys[i]], keys[i]); 5393 } 5394 return keys; 5395} 5396 5397 5398/** 5399 * when using forEach the params are value, key, but it is often useful to have key, value. 5400 * @param {function(string, *)} iteratorFn 5401 * @returns {function(*, string)} 5402 */ 5403function reverseParams(iteratorFn) { 5404 return function(value, key) { iteratorFn(key, value); }; 5405} 5406 5407/** 5408 * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric 5409 * characters such as '012ABC'. The reason why we are not using simply a number counter is that 5410 * the number string gets longer over time, and it can also overflow, where as the nextId 5411 * will grow much slower, it is a string, and it will never overflow. 5412 * 5413 * @returns {string} an unique alpha-numeric string 5414 */ 5415function nextUid() { 5416 var index = uid.length; 5417 var digit; 5418 5419 while(index) { 5420 index--;
5421 digit = uid[index].charCodeAt(0); 5422 if (digit == 57 /*'9'*/) { 5423 uid[index] = 'A'; 5424 return uid.join(''); 5425 } 5426 if (digit == 90 /*'Z'*/) { 5427 uid[index] = '0'; 5428 } else { 5429 uid[index] = String.fromCharCode(digit + 1); 5430 return uid.join(''); 5431 } 5432 } 5433 uid.unshift('0'); 5434 return uid.join(''); 5435} 5436 5437 5438/** 5439 * Set or clear the hashkey for an object. 5440 * @param obj object 5441 * @param h the hashkey (!truthy to delete the hashkey) 5442 */ 5443function setHashKey(obj, h) { 5444 if (h) { 5445 obj.$$hashKey = h; 5446 } 5447 else { 5448 delete obj.$$hashKey; 5449 } 5450} 5451 5452/** 5453 * @ngdoc function 5454 * @name angular.extend 5455 * @module ng 5456 * @kind function 5457 * 5458 * @description 5459 * Extends the destination object `dst` by copying all of the properties from the `src` object(s) 5460 * to `dst`. You can specify multiple `src` objects. 5461 * 5462 * @param {Object} dst Destination object. 5463 * @param {...Object} src Source object(s). 5464 * @returns {Object} Reference to `dst`. 5465 */ 5466function extend(dst) { 5467 var h = dst.$$hashKey; 5468 forEach(arguments, function(obj) { 5469 if (obj !== dst) { 5470 forEach(obj, function(value, key) { 5471 dst[key] = value; 5472 }); 5473 } 5474 }); 5475 5476 setHashKey(dst,h); 5477 return dst; 5478} 5479 5480function int(str) { 5481 return parseInt(str, 10); 5482} 5483 5484 5485function inherit(parent, extra) { 5486 return extend(new (extend(function() {}, {prototype:parent}))(), extra); 5487} 5488 5489/** 5490 * @ngdoc function 5491 * @name angular.noop 5492 * @module ng 5493 * @kind function 5494 * 5495 * @description 5496 * A function that performs no operations. This function can be useful when writing code in the 5497 * functional style. 5498 ```js 5499 function foo(callback) { 5500 var result = calculateResult(); 5501 (callback || angular.noop)(result); 5502 } 5503 ``` 5504 */ 5505function noop() {} 5506noop.$inject = []; 5507 5508 5509/** 5510 * @ngdoc function 5511 * @name angular.identity 5512 * @module ng 5513 * @kind function 5514 * 5515 * @description 5516 * A function that returns its first argument. This function is useful when writing code in the 5517 * functional style. 5518 * 5519 ```js 5520 function transformer(transformationFn, value) { 5521 return (transformationFn || angular.identity)(value); 5522 }; 5523 ``` 5524 */ 5525function identity($) {return $;} 5526identity.$inject = []; 5527 5528 5529function valueFn(value) {return function() {return value;};} 5530 5531/** 5532 * @ngdoc function 5533 * @name angular.isUndefined 5534 * @module ng 5535 * @kind function 5536 * 5537 * @description 5538 * Determines if a reference is undefined. 5539 * 5540 * @param {*} value Reference to check. 5541 * @returns {boolean} True if `value` is undefined. 5542 */ 5543function isUndefined(value){return typeof value === 'undefined';} 5544 5545 5546/** 5547 * @ngdoc function 5548 * @name angular.isDefined 5549 * @module ng 5550 * @kind function 5551 * 5552 * @description 5553 * Determines if a reference is defined. 5554 * 5555 * @param {*} value Reference to check. 5556 * @returns {boolean} True if `value` is defined. 5557 */ 5558function isDefined(value){return typeof value !== 'undefined';} 5559 5560 5561/** 5562 * @ngdoc function 5563 * @name angular.isObject 5564 * @module ng 5565 * @kind function 5566 * 5567 * @description 5568 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not 5569 * considered to be objects. Note that JavaScript arrays are objects. 5570 * 5571 * @param {*} value Reference to check. 5572 * @returns {boolean} True if `value` is an `Object` but not `null`. 5573 */ 5574function isObject(value){return value != null && typeof value === 'object';} 5575 5576 5577/** 5578 * @ngdoc function 5579 * @name angular.isString 5580 * @module ng 5581 * @kind function 5582 * 5583 * @description 5584 * Determines if a reference is a `String`. 5585 * 5586 * @param {*} value Reference to check. 5587 * @returns {boolean} True if `value` is a `String`. 5588 */ 5589function isString(value){return typeof value === 'string';} 5590 5591 5592/** 5593 * @ngdoc function 5594 * @name angular.isNumber 5595 * @module ng 5596 * @kind function 5597 * 5598 * @description 5599 * Determines if a reference is a `Number`. 5600 * 5601 * @param {*} value Reference to check. 5602 * @returns {boolean} True if `value` is a `Number`. 5603 */ 5604function isNumber(value){return typeof value === 'number';} 5605 5606 5607/** 5608 * @ngdoc function 5609 * @name angular.isDate 5610 * @module ng 5611 * @kind function 5612 * 5613 * @description 5614 * Determines if a value is a date. 5615 * 5616 * @param {*} value Reference to check. 5617 * @returns {boolean} True if `value` is a `Date`. 5618 */ 5619function isDate(value) { 5620 return toString.call(value) === '[object Date]'; 5621} 5622 5623 5624/** 5625 * @ngdoc function 5626 * @name angular.isArray 5627 * @module ng 5628 * @kind function 5629 * 5630 * @description 5631 * Determines if a reference is an `Array`. 5632 * 5633 * @param {*} value Reference to check. 5634 * @returns {boolean} True if `value` is an `Array`. 5635 */ 5636var isArray = (function() { 5637 if (!isFunction(Array.isArray)) { 5638 return function(value) { 5639 return toString.call(value) === '[object Array]'; 5640 }; 5641 } 5642 return Array.isArray; 5643})(); 5644 5645/** 5646 * @ngdoc function 5647 * @name angular.isFunction 5648 * @module ng 5649 * @kind function 5650 * 5651 * @description 5652 * Determines if a reference is a `Function`. 5653 * 5654 * @param {*} value Reference to check. 5655 * @returns {boolean} True if `value` is a `Function`. 5656 */ 5657function isFunction(value){return typeof value === 'function';} 5658 5659 5660/** 5661 * Determines if a value is a regular expression object. 5662 * 5663 * @private 5664 * @param {*} value Reference to check. 5665 * @returns {boolean} True if `value` is a `RegExp`. 5666 */ 5667function isRegExp(value) { 5668 return toString.call(value) === '[object RegExp]'; 5669} 5670 5671 5672/** 5673 * Checks if `obj` is a window object. 5674 * 5675 * @private 5676 * @param {*} obj Object to check 5677 * @returns {boolean} True if `obj` is a window obj. 5678 */ 5679function isWindow(obj) { 5680 return obj && obj.document && obj.location && obj.alert && obj.setInterval; 5681} 5682 5683 5684function isScope(obj) { 5685 return obj && obj.$evalAsync && obj.$watch; 5686} 5687 5688 5689function isFile(obj) { 5690 return toString.call(obj) === '[object File]'; 5691} 5692 5693 5694function isBlob(obj) { 5695 return toString.call(obj) === '[object Blob]'; 5696} 5697 5698 5699function isBoolean(value) { 5700 return typeof value === 'boolean'; 5701} 5702 5703 5704function isPromiseLike(obj) { 5705 return obj && isFunction(obj.then); 5706} 5707 5708 5709var trim = (function() { 5710 // native trim is way faster: http://jsperf.com/angular-trim-test 5711 // but IE doesn't have it... :-( 5712 // TODO: we should move this into IE/ES5 polyfill 5713 if (!String.prototype.trim) { 5714 return function(value) { 5715 return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; 5716 }; 5717 } 5718 return function(value) { 5719 return isString(value) ? value.trim() : value; 5720 }; 5721})(); 5722 5723 5724/** 5725 * @ngdoc function 5726 * @name angular.isElement 5727 * @module ng 5728 * @kind function 5729 * 5730 * @description 5731 * Determines if a reference is a DOM element (or wrapped jQuery element). 5732 * 5733 * @param {*} value Reference to check. 5734 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). 5735 */ 5736function isElement(node) { 5737 return !!(node && 5738 (node.nodeName // we are a direct element 5739 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API 5740} 5741 5742/** 5743 * @param str 'key1,key2,...' 5744 * @returns {object} in the form of {key1:true, key2:true, ...} 5745 */ 5746function makeMap(str) { 5747 var obj = {}, items = str.split(","), i; 5748 for ( i = 0; i < items.length; i++ ) 5749 obj[ items[i] ] = true; 5750 return obj;
5751} 5752 5753 5754if (msie < 9) { 5755 nodeName_ = function(element) { 5756 element = element.nodeName ? element : element[0]; 5757 return (element.scopeName && element.scopeName != 'HTML') 5758 ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; 5759 }; 5760} else { 5761 nodeName_ = function(element) { 5762 return element.nodeName ? element.nodeName : element[0].nodeName; 5763 }; 5764} 5765 5766 5767function map(obj, iterator, context) { 5768 var results = []; 5769 forEach(obj, function(value, index, list) { 5770 results.push(iterator.call(context, value, index, list)); 5771 }); 5772 return results; 5773} 5774 5775 5776/** 5777 * @description 5778 * Determines the number of elements in an array, the number of properties an object has, or 5779 * the length of a string. 5780 * 5781 * Note: This function is used to augment the Object type in Angular expressions. See 5782 * {@link angular.Object} for more information about Angular arrays. 5783 * 5784 * @param {Object|Array|string} obj Object, array, or string to inspect. 5785 * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object 5786 * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. 5787 */ 5788function size(obj, ownPropsOnly) { 5789 var count = 0, key; 5790 5791 if (isArray(obj) || isString(obj)) { 5792 return obj.length; 5793 } else if (isObject(obj)) { 5794 for (key in obj) 5795 if (!ownPropsOnly || obj.hasOwnProperty(key)) 5796 count++; 5797 } 5798 5799 return count; 5800} 5801 5802 5803function includes(array, obj) { 5804 return indexOf(array, obj) != -1; 5805} 5806 5807function indexOf(array, obj) { 5808 if (array.indexOf) return array.indexOf(obj); 5809 5810 for (var i = 0; i < array.length; i++) { 5811 if (obj === array[i]) return i; 5812 } 5813 return -1; 5814} 5815 5816function arrayRemove(array, value) { 5817 var index = indexOf(array, value); 5818 if (index >=0) 5819 array.splice(index, 1); 5820 return value; 5821} 5822 5823function isLeafNode (node) { 5824 if (node) { 5825 switch (node.nodeName) { 5826 case "OPTION": 5827 case "PRE": 5828 case "TITLE": 5829 return true; 5830 } 5831 } 5832 return false; 5833} 5834 5835/** 5836 * @ngdoc function 5837 * @name angular.copy 5838 * @module ng 5839 * @kind function 5840 * 5841 * @description 5842 * Creates a deep copy of `source`, which should be an object or an array. 5843 * 5844 * * If no destination is supplied, a copy of the object or array is created. 5845 * * If a destination is provided, all of its elements (for array) or properties (for objects) 5846 * are deleted and then all elements/properties from the source are copied to it. 5847 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. 5848 * * If `source` is identical to 'destination' an exception will be thrown. 5849 * 5850 * @param {*} source The source that will be used to make a copy. 5851 * Can be any type, including primitives, `null`, and `undefined`. 5852 * @param {(Object|Array)=} destination Destination into which the source is copied. If 5853 * provided, must be of the same type as `source`. 5854 * @returns {*} The copy or updated `destination`, if `destination` was specified. 5855 * 5856 * @example 5857 <example module="copyExample"> 5858 <file name="index.html">
5859 <div ng-controller="ExampleController"> 5860 <form novalidate class="simple-form"> 5861 Name: <input type="text" ng-model="user.name" /><br /> 5862 E-mail: <input type="email" ng-model="user.email" /><br /> 5863 Gender: <input type="radio" ng-model="user.gender" value="male" />male 5864 <input type="radio" ng-model="user.gender" value="female" />female<br /> 5865 <button ng-click="reset()">RESET</button> 5866 <button ng-click="update(user)">SAVE</button> 5867 </form> 5868 <pre>form = {{user | json}}</pre> 5869 <pre>master = {{master | json}}</pre> 5870 </div> 5871 5872 <script> 5873 angular.module('copyExample', []) 5874 .controller('ExampleController', ['$scope', function($scope) { 5875 $scope.master= {}; 5876 5877 $scope.update = function(user) { 5878 // Example with 1 argument 5879 $scope.master= angular.copy(user); 5880 }; 5881 5882 $scope.reset = function() { 5883 // Example with 2 arguments 5884 angular.copy($scope.master, $scope.user); 5885 }; 5886 5887 $scope.reset(); 5888 }]); 5889 </script> 5890 </file> 5891 </example> 5892 */ 5893function copy(source, destination, stackSource, stackDest) { 5894 if (isWindow(source) || isScope(source)) { 5895 throw ngMinErr('cpws', 5896 "Can't copy! Making copies of Window or Scope instances is not supported."); 5897 } 5898 5899 if (!destination) { 5900 destination = source; 5901 if (source) { 5902 if (isArray(source)) { 5903 destination = copy(source, [], stackSource, stackDest); 5904 } else if (isDate(source)) { 5905 destination = new Date(source.getTime()); 5906 } else if (isRegExp(source)) { 5907 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); 5908 destination.lastIndex = source.lastIndex; 5909 } else if (isObject(source)) { 5910 destination = copy(source, {}, stackSource, stackDest); 5911 } 5912 } 5913 } else { 5914 if (source === destination) throw ngMinErr('cpi', 5915 "Can't copy! Source and destination are identical."); 5916 5917 stackSource = stackSource || []; 5918 stackDest = stackDest || []; 5919 5920 if (isObject(source)) { 5921 var index = indexOf(stackSource, source); 5922 if (index !== -1) return stackDest[index]; 5923 5924 stackSource.push(source); 5925 stackDest.push(destination); 5926 } 5927 5928 var result; 5929 if (isArray(source)) { 5930 destination.length = 0; 5931 for ( var i = 0; i < source.length; i++) { 5932 result = copy(source[i], null, stackSource, stackDest); 5933 if (isObject(source[i])) { 5934 stackSource.push(source[i]); 5935 stackDest.push(result); 5936 } 5937 destination.push(result); 5938 } 5939 } else { 5940 var h = destination.$$hashKey; 5941 if (isArray(destination)) { 5942 destination.length = 0; 5943 } else { 5944 forEach(destination, function(value, key) { 5945 delete destination[key]; 5946 }); 5947 } 5948 for ( var key in source) { 5949 result = copy(source[key], null, stackSource, stackDest); 5950 if (isObject(source[key])) { 5951 stackSource.push(source[key]); 5952 stackDest.push(result); 5953 } 5954 destination[key] = result; 5955 } 5956 setHashKey(destination,h); 5957 } 5958 5959 } 5960 return destination; 5961} 5962 5963/** 5964 * Creates a shallow copy of an object, an array or a primitive 5965 */ 5966function shallowCopy(src, dst) { 5967 if (isArray(src)) { 5968 dst = dst || []; 5969 5970 for ( var i = 0; i < src.length; i++) { 5971 dst[i] = src[i];
5972 } 5973 } else if (isObject(src)) { 5974 dst = dst || {}; 5975 5976 for (var key in src) { 5977 if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { 5978 dst[key] = src[key]; 5979 } 5980 } 5981 } 5982 5983 return dst || src; 5984} 5985 5986 5987/** 5988 * @ngdoc function 5989 * @name angular.equals 5990 * @module ng 5991 * @kind function 5992 * 5993 * @description 5994 * Determines if two objects or two values are equivalent. Supports value types, regular 5995 * expressions, arrays and objects. 5996 * 5997 * Two objects or values are considered equivalent if at least one of the following is true: 5998 * 5999 * * Both objects or values pass `===` comparison. 6000 * * Both objects or values are of the same type and all of their properties are equal by 6001 * comparing them with `angular.equals`. 6002 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) 6003 * * Both values represent the same regular expression (In JavaScript, 6004 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual 6005 * representation matches). 6006 * 6007 * During a property comparison, properties of `function` type and properties with names 6008 * that begin with `$` are ignored. 6009 * 6010 * Scope and DOMWindow objects are being compared only by identify (`===`). 6011 * 6012 * @param {*} o1 Object or value to compare. 6013 * @param {*} o2 Object or value to compare. 6014 * @returns {boolean} True if arguments are equal. 6015 */ 6016function equals(o1, o2) { 6017 if (o1 === o2) return true; 6018 if (o1 === null || o2 === null) return false; 6019 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN 6020 var t1 = typeof o1, t2 = typeof o2, length, key, keySet; 6021 if (t1 == t2) { 6022 if (t1 == 'object') { 6023 if (isArray(o1)) { 6024 if (!isArray(o2)) return false; 6025 if ((length = o1.length) == o2.length) { 6026 for(key=0; key<length; key++) { 6027 if (!equals(o1[key], o2[key])) return false; 6028 } 6029 return true; 6030 } 6031 } else if (isDate(o1)) { 6032 if (!isDate(o2)) return false; 6033 return (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime()); 6034 } else if (isRegExp(o1) && isRegExp(o2)) { 6035 return o1.toString() == o2.toString(); 6036 } else { 6037 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false; 6038 keySet = {}; 6039 for(key in o1) { 6040 if (key.charAt(0) === '$' || isFunction(o1[key])) continue; 6041 if (!equals(o1[key], o2[key])) return false; 6042 keySet[key] = true; 6043 } 6044 for(key in o2) { 6045 if (!keySet.hasOwnProperty(key) && 6046 key.charAt(0) !== '$' && 6047 o2[key] !== undefined && 6048 !isFunction(o2[key])) return false; 6049 } 6050 return true; 6051 } 6052 } 6053 } 6054 return false; 6055} 6056 6057var csp = function() { 6058 if (isDefined(csp.isActive_)) return csp.isActive_; 6059 6060 var active = !!(document.querySelector('[ng-csp]') || 6061 document.querySelector('[data-ng-csp]')); 6062 6063 if (!active) { 6064 try { 6065 /* jshint -W031, -W054 */ 6066 new Function(''); 6067 /* jshint +W031, +W054 */ 6068 } catch (e) { 6069 active = true; 6070 } 6071 } 6072 6073 return (csp.isActive_ = active); 6074}; 6075 6076 6077 6078function concat(array1, array2, index) { 6079 return array1.concat(slice.call(array2, index)); 6080} 6081 6082function sliceArgs(args, startIndex) { 6083 return slice.call(args, startIndex || 0); 6084} 6085 6086 6087/* jshint -W101 */ 6088/** 6089 * @ngdoc function 6090 * @name angular.bind 6091 * @module ng 6092 * @kind function 6093 * 6094 * @description 6095 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for 6096 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also 6097 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as 6098 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). 6099 * 6100 * @param {Object} self Context which `fn` should be evaluated in. 6101 * @param {function()} fn Function to be bound. 6102 * @param {...*} args Optional arguments to be prebound to the `fn` function call. 6103 * @returns {function()} Function that wraps the `fn` with all the specified bindings. 6104 */ 6105/* jshint +W101 */ 6106function bind(self, fn) { 6107 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; 6108 if (isFunction(fn) && !(fn instanceof RegExp)) { 6109 return curryArgs.length 6110 ? function() { 6111 return arguments.length 6112 ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) 6113 : fn.apply(self, curryArgs); 6114 } 6115 : function() { 6116 return arguments.length 6117 ? fn.apply(self, arguments) 6118 : fn.call(self); 6119 }; 6120 } else { 6121 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) 6122 return fn; 6123 } 6124} 6125 6126 6127function toJsonReplacer(key, value) { 6128 var val = value; 6129 6130 if (typeof key === 'string' && key.charAt(0) === '$') { 6131 val = undefined; 6132 } else if (isWindow(value)) { 6133 val = '$WINDOW'; 6134 } else if (value && document === value) { 6135 val = '$DOCUMENT'; 6136 } else if (isScope(value)) { 6137 val = '$SCOPE'; 6138 } 6139 6140 return val; 6141} 6142 6143 6144/** 6145 * @ngdoc function 6146 * @name angular.toJson 6147 * @module ng 6148 * @kind function 6149 * 6150 * @description 6151 * Serializes input into a JSON-formatted string. Properties with leading $ characters will be 6152 * stripped since angular uses this notation internally. 6153 * 6154 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. 6155 * @param {boolean=}
6155 pretty If set to true, the JSON output will contain newlines and whitespace. 6156 * @returns {string|undefined} JSON-ified string representing `obj`. 6157 */ 6158function toJson(obj, pretty) { 6159 if (typeof obj === 'undefined') return undefined; 6160 return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); 6161} 6162 6163 6164/** 6165 * @ngdoc function 6166 * @name angular.fromJson 6167 * @module ng 6168 * @kind function 6169 * 6170 * @description 6171 * Deserializes a JSON string. 6172 * 6173 * @param {string} json JSON string to deserialize. 6174 * @returns {Object|Array|string|number} Deserialized thingy. 6175 */ 6176function fromJson(json) { 6177 return isString(json) 6178 ? JSON.parse(json) 6179 : json; 6180} 6181 6182 6183function toBoolean(value) { 6184 if (typeof value === 'function') { 6185 value = true; 6186 } else if (value && value.length !== 0) { 6187 var v = lowercase("" + value); 6188 value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); 6189 } else { 6190 value = false; 6191 } 6192 return value; 6193} 6194 6195/** 6196 * @returns {string} Returns the string representation of the element. 6197 */ 6198function startingTag(element) { 6199 element = jqLite(element).clone(); 6200 try { 6201 // turns out IE does not let you set .html() on elements which 6202 // are not allowed to have children. So we just ignore it. 6203 element.empty(); 6204 } catch(e) {} 6205 // As Per DOM Standards 6206 var TEXT_NODE = 3; 6207 var elemHtml = jqLite('<div>').append(element).html(); 6208 try { 6209 return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : 6210 elemHtml. 6211 match(/^(<[^>]+>)/)[1]. 6212 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); 6213 } catch(e) { 6214 return lowercase(elemHtml); 6215 } 6216 6217} 6218 6219 6220///////////////////////////////////////////////// 6221 6222/** 6223 * Tries to decode the URI component without throwing an exception. 6224 * 6225 * @private 6226 * @param str value potential URI component to check. 6227 * @returns {boolean} True if `value` can be decoded 6228 * with the decodeURIComponent function. 6229 */ 6230function tryDecodeURIComponent(value) { 6231 try { 6232 return decodeURIComponent(value); 6233 } catch(e) { 6234 // Ignore any invalid uri component 6235 } 6236} 6237 6238 6239/** 6240 * Parses an escaped url query string into key-value pairs. 6241 * @returns {Object.<string,boolean|Array>} 6242 */ 6243function parseKeyValue(/**string*/keyValue) { 6244 var obj = {}, key_value, key; 6245 forEach((keyValue || "").split('&'), function(keyValue) { 6246 if ( keyValue ) { 6247 key_value = keyValue.replace(/\+/g,'%20').split('='); 6248 key = tryDecodeURIComponent(key_value[0]); 6249 if ( isDefined(key) ) { 6250 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; 6251 if (!hasOwnProperty.call(obj, key)) { 6252 obj[key] = val; 6253 } else if(isArray(obj[key])) { 6254 obj[key].push(val); 6255 } else { 6256 obj[key] = [obj[key],val]; 6257 } 6258 } 6259 } 6260 }); 6261 return obj; 6262} 6263 6264function toKeyValue(obj) { 6265 var parts = []; 6266 forEach(obj, function(value, key) { 6267 if (isArray(value)) { 6268 forEach(value, function(arrayValue) { 6269 parts.push(encodeUriQuery(key, true) + 6270 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); 6271 }); 6272 } else { 6273 parts.push(encodeUriQuery(key, true) + 6274 (value === true ? '' : '=' + encodeUriQuery(value, true))); 6275 } 6276 }); 6277 return parts.length ? parts.join('&') : ''; 6278} 6279 6280 6281/** 6282 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow 6283 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path 6284 * segments: 6285 * segment = *pchar 6286 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 6287 * pct-encoded = "%" HEXDIG HEXDIG 6288 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 6289 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 6290 * / "*" / "+" / "," / ";" / "=" 6291 */ 6292function encodeUriSegment(val) { 6293 return encodeUriQuery(val, true). 6294 replace(/%26/gi, '&'). 6295 replace(/%3D/gi, '='). 6296 replace(/%2B/gi, '+'); 6297} 6298 6299 6300/** 6301 * This method is intended for encoding *key* or *value* parts of query component. We need a custom 6302 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be 6303 * encoded per http://tools.ietf.org/html/rfc3986: 6304 * query = *( pchar / "/" / "?" ) 6305 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 6306 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 6307 * pct-encoded = "%" HEXDIG HEXDIG 6308 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 6309 * / "*" / "+" / "," / ";" / "=" 6310 */ 6311function encodeUriQuery(val, pctEncodeSpaces) { 6312 return encodeURIComponent(val). 6313 replace(/%40/gi, '@'). 6314 replace(/%3A/gi, ':'). 6315 replace(/%24/g, '$'). 6316 replace(/%2C/gi, ','). 6317 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); 6318} 6319 6320 6321/** 6322 * @ngdoc directive 6323 * @name ngApp 6324 * @module ng 6325 * 6326 * @element ANY 6327 * @param {angular.Module} ngApp an optional application 6328 * {@link angular.module module} name to load. 6329 * 6330 * @description 6331 * 6332 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive 6333 * designates the **root element** of the application and is typically placed near the root element 6334 * of the page - e.g. on the `<body>` or `<html>` tags. 6335 * 6336 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngAp
6336p` 6337 * found in the document will be used to define the root element to auto-bootstrap as an 6338 * application. To run multiple applications in an HTML document you must manually bootstrap them using 6339 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. 6340 * 6341 * You can specify an **AngularJS module** to be used as the root module for the application. This 6342 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and 6343 * should contain the application code needed or have dependencies on other modules that will 6344 * contain the code. See {@link angular.module} for more information. 6345 * 6346 * In the example below if the `ngApp` directive were not placed on the `html` element then the 6347 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` 6348 * would not be resolved to `3`. 6349 * 6350 * `ngApp` is the easiest, and most common, way to bootstrap an application. 6351 * 6352 <example module="ngAppDemo"> 6353 <file name="index.html"> 6354 <div ng-controller="ngAppDemoController"> 6355 I can add: {{a}} + {{b}} = {{ a+b }} 6356 </div> 6357 </file> 6358 <file name="script.js"> 6359 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { 6360 $scope.a = 1; 6361 $scope.b = 2; 6362 }); 6363 </file> 6364 </example> 6365 * 6366 */ 6367function angularInit(element, bootstrap) { 6368 var elements = [element], 6369 appElement, 6370 module, 6371 names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], 6372 NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; 6373 6374 function append(element) { 6375 element && elements.push(element); 6376 } 6377 6378 forEach(names, function(name) { 6379 names[name] = true; 6380 append(document.getElementById(name)); 6381 name = name.replace(':', '\\:'); 6382 if (element.querySelectorAll) { 6383 forEach(element.querySelectorAll('.' + name), append); 6384 forEach(element.querySelectorAll('.' + name + '\\:'), append); 6385 forEach(element.querySelectorAll('[' + name + ']'), append); 6386 } 6387 }); 6388 6389 forEach(elements, function(element) { 6390 if (!appElement) { 6391 var className = ' ' + element.className + ' '; 6392 var match = NG_APP_CLASS_REGEXP.exec(className); 6393 if (match) { 6394 appElement = element; 6395 module = (match[2] || '').replace(/\s+/g, ','); 6396 } else { 6397 forEach(element.attributes, function(attr) { 6398 if (!appElement && names[attr.name]) { 6399 appElement = element; 6400 module = attr.value; 6401 } 6402 }); 6403 } 6404 } 6405 }); 6406 if (appElement) { 6407 bootstrap(appElement, module ? [module] : []); 6408 } 6409} 6410 6411/** 6412 * @ngdoc function 6413 * @name angular.bootstrap 6414 * @module ng 6415 * @description 6416 * Use this function to manually start up angular application. 6417 * 6418 * See: {@link guide/bootstrap Bootstrap} 6419 * 6420 * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually. 6421 * They must use {@link ng.directive:ngApp ngApp}. 6422 * 6423 * Angular will detect if it has been loaded into the browser more than once and only allow the 6424 * first loaded script to be bootstrapped and will report a warning to the browser console for 6425 * each of the subsequent scripts. This prevents strange results in applications, where otherwise 6426 * multiple instances of Angular try to work on the DOM. 6427 * 6428 * <example name="multi-bootstrap" module="multi-bootstrap"> 6429 * <file name="index.html"> 6430 * <script src="../../../angular.js"></script> 6431 * <div ng-controller="BrokenTable"> 6432 * <table> 6433 * <tr> 6434 * <th ng-repeat="heading in headings">{{heading}}</th> 6435 * </tr> 6436 * <tr ng-repeat="filling in fillings"> 6437 * <td ng-repeat="fill in filling">{{fill}}</td> 6438 * </tr> 6439 * </table> 6440 * </div> 6441 * </file> 6442 * <file name="controller.js"> 6443 * var app = angular.module('multi-bootstrap', []) 6444 * 6445 * .controller('BrokenTable', function($scope) { 6446 * $scope.headings = ['One', 'Two', 'Three']; 6447 * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]]; 6448 * }); 6449 * </file> 6450 * <file name="protractor.js" type="protractor"> 6451 * it('should only insert one table cell for each item in $scope.fillings', function() { 6452 * expect(element.all(by.css('td')).count()) 6453 * .toBe(9); 6454 * }); 6455 * </file> 6456 * </example> 6457 * 6458 * @param {DOMElement} element DOM element which is the root of angular application. 6459 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. 6460 * Each item in the array should be the name of a predefined module or a (DI annotated) 6461 * function that will be invoked by the injector as a run block. 6462 * See: {@link angular.module modules} 6463 * @returns {auto.$injector} Returns the newly created injector for this app. 6464 */ 6465function bootstrap(element, modules) { 6466 var doBootstrap = function() { 6467 element = jqLite(element); 6468 6469 if (element.injector()) { 6470 var tag = (element[0] === document) ? 'document' : startingTag(element); 6471 //Encode angle brackets to prevent input from being sanitized to empty string #8683 6472 throw ngMinErr( 6473 'btstrpd', 6474 "App Already Bootstrapped with this Element '{0}'", 6475 tag.replace(/</,'<').replace(/>/,'>')); 6476 } 6477 6478 modules = modules || []; 6479 modules.unshift(['$provide', function($provide) { 6480 $provide.value('$rootElement', element); 6481 }]); 6482 modules.unshift('ng'); 6483 var injector = createInjector(modules); 6484 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate', 6485 function(scope, element, compile, injector, animate) { 6486 scope.$apply(function() { 6487 element.data('$injector', injector); 6488 compile(element)(scope); 6489 }); 6490 }] 6491 ); 6492 return injector; 6493 }; 6494 6495 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; 6496 6497 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { 6498 return doBootstrap(); 6499 } 6500 6501 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); 6502 angular.resumeBootstrap = function(extraModules) {
6503 forEach(extraModules, function(module) { 6504 modules.push(module); 6505 }); 6506 doBootstrap(); 6507 }; 6508} 6509 6510var SNAKE_CASE_REGEXP = /[A-Z]/g; 6511function snake_case(name, separator) { 6512 separator = separator || '_'; 6513 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { 6514 return (pos ? separator : '') + letter.toLowerCase(); 6515 }); 6516} 6517 6518function bindJQuery() { 6519 // bind to jQuery if present; 6520 jQuery = window.jQuery; 6521 // Use jQuery if it exists with proper functionality, otherwise default to us. 6522 // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support. 6523 if (jQuery && jQuery.fn.on) { 6524 jqLite = jQuery; 6525 extend(jQuery.fn, { 6526 scope: JQLitePrototype.scope, 6527 isolateScope: JQLitePrototype.isolateScope, 6528 controller: JQLitePrototype.controller, 6529 injector: JQLitePrototype.injector, 6530 inheritedData: JQLitePrototype.inheritedData 6531 }); 6532 // Method signature: 6533 // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) 6534 jqLitePatchJQueryRemove('remove', true, true, false); 6535 jqLitePatchJQueryRemove('empty', false, false, false); 6536 jqLitePatchJQueryRemove('html', false, false, true); 6537 } else { 6538 jqLite = JQLite; 6539 } 6540 angular.element = jqLite; 6541} 6542 6543/** 6544 * throw error if the argument is falsy. 6545 */ 6546function assertArg(arg, name, reason) { 6547 if (!arg) { 6548 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); 6549 } 6550 return arg; 6551} 6552 6553function assertArgFn(arg, name, acceptArrayAnnotation) { 6554 if (acceptArrayAnnotation && isArray(arg)) { 6555 arg = arg[arg.length - 1]; 6556 } 6557 6558 assertArg(isFunction(arg), name, 'not a function, got ' + 6559 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); 6560 return arg; 6561} 6562 6563/** 6564 * throw error if the name given is hasOwnProperty 6565 * @param {String} name the name to test 6566 * @param {String} context the context in which the name is used, such as module or directive 6567 */ 6568function assertNotHasOwnProperty(name, context) { 6569 if (name === 'hasOwnProperty') { 6570 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); 6571 } 6572} 6573 6574/** 6575 * Return the value accessible from the object by path. Any undefined traversals are ignored 6576 * @param {Object} obj starting object 6577 * @param {String} path path to traverse 6578 * @param {boolean} [bindFnToScope=true] 6579 * @returns {Object} value as accessible by path 6580 */ 6581//TODO(misko): this function needs to be removed 6582function getter(obj, path, bindFnToScope) { 6583 if (!path) return obj; 6584 var keys = path.split('.'); 6585 var key; 6586 var lastInstance = obj; 6587 var len = keys.length; 6588 6589 for (var i = 0; i < len; i++) { 6590 key = keys[i]; 6591 if (obj) { 6592 obj = (lastInstance = obj)[key]; 6593 } 6594 } 6595 if (!bindFnToScope && isFunction(obj)) { 6596 return bind(lastInstance, obj); 6597 } 6598 return obj; 6599} 6600 6601/** 6602 * Return the DOM siblings between the first and last node in the given array. 6603 * @param {Array} array like object 6604 * @returns {DOMElement}
6604 object containing the elements 6605 */ 6606function getBlockElements(nodes) { 6607 var startNode = nodes[0], 6608 endNode = nodes[nodes.length - 1]; 6609 if (startNode === endNode) { 6610 return jqLite(startNode); 6611 } 6612 6613 var element = startNode; 6614 var elements = [element]; 6615 6616 do { 6617 element = element.nextSibling; 6618 if (!element) break; 6619 elements.push(element); 6620 } while (element !== endNode); 6621 6622 return jqLite(elements); 6623} 6624 6625/** 6626 * @ngdoc type 6627 * @name angular.Module 6628 * @module ng 6629 * @description 6630 * 6631 * Interface for configuring angular {@link angular.module modules}. 6632 */ 6633 6634function setupModuleLoader(window) { 6635 6636 var $injectorMinErr = minErr('$injector'); 6637 var ngMinErr = minErr('ng'); 6638 6639 function ensure(obj, name, factory) { 6640 return obj[name] || (obj[name] = factory()); 6641 } 6642 6643 var angular = ensure(window, 'angular', Object); 6644 6645 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap 6646 angular.$$minErr = angular.$$minErr || minErr; 6647 6648 return ensure(angular, 'module', function() { 6649 /** @type {Object.<string, angular.Module>} */ 6650 var modules = {}; 6651 6652 /** 6653 * @ngdoc function 6654 * @name angular.module 6655 * @module ng 6656 * @description 6657 * 6658 * The `angular.module` is a global place for creating, registering and retrieving Angular 6659 * modules. 6660 * All modules (angular core or 3rd party) that should be available to an application must be 6661 * registered using this mechanism. 6662 * 6663 * When passed two or more arguments, a new module is created. If passed only one argument, an 6664 * existing module (the name passed as the first argument to `module`) is retrieved. 6665 * 6666 * 6667 * # Module 6668 * 6669 * A module is a collection of services, directives, controllers, filters, and configuration information. 6670 * `angular.module` is used to configure the {@link auto.$injector $injector}. 6671 * 6672 * ```js 6673 * // Create a new module 6674 * var myModule = angular.module('myModule', []); 6675 * 6676 * // register a new service 6677 * myModule.value('appName', 'MyCoolApp'); 6678 * 6679 * // configure existing services inside initialization blocks. 6680 * myModule.config(['$locationProvider', function($locationProvider) { 6681 * // Configure existing providers 6682 * $locationProvider.hashPrefix('!'); 6683 * }]); 6684 * ``` 6685 * 6686 * Then you can create an injector and load your modules like this: 6687 * 6688 * ```js 6689 * var injector = angular.injector(['ng', 'myModule']) 6690 * ``` 6691 * 6692 * However it's more likely that you'll just use 6693 * {@link ng.directive:ngApp ngApp} or 6694 * {@link angular.bootstrap} to simplify this process for you. 6695 * 6696 * @param {!string} name The name of the module to create or retrieve. 6697 * @param {!Array.<string>=} requires If specified then new module is being created. If 6698 * unspecified then the module is being retrieved for further configuration. 6699 * @param {Function=} configFn Optional configuration function for the module. Same as 6700 * {@link angular.Module#config Module#config()}. 6701 * @returns {module} new module with the {@link angular.Module} api. 6702 */ 6703 return function module(name, requires, configFn) { 6704 var assertNotHasOwnProperty = function(name, context) { 6705 if (name === 'hasOwnProperty') { 6706 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); 6707 } 6708 }; 6709 6710 assertNotHasOwnProperty(name, 'module'); 6711 if (requires && modules.hasOwnProperty(name)) { 6712 modules[name] = null; 6713 } 6714 return ensure(modules, name, function() { 6715 if (!requires) { 6716 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + 6717 "the module name or forgot to load it. If registering a module ensure that you " + 6718 "specify the dependencies as the second argument.", name); 6719 } 6720 6721 /** @type {!Array.<Array.<*>>} */ 6722 var invokeQueue = []; 6723
6724 /** @type {!Array.<Function>} */ 6725 var runBlocks = []; 6726 6727 var config = invokeLater('$injector', 'invoke'); 6728 6729 /** @type {angular.Module} */ 6730 var moduleInstance = { 6731 // Private state 6732 _invokeQueue: invokeQueue, 6733 _runBlocks: runBlocks, 6734 6735 /** 6736 * @ngdoc property 6737 * @name angular.Module#requires 6738 * @module ng 6739 * 6740 * @description 6741 * Holds the list of modules which the injector will load before the current module is 6742 * loaded. 6743 */ 6744 requires: requires, 6745 6746 /** 6747 * @ngdoc property 6748 * @name angular.Module#name 6749 * @module ng 6750 * 6751 * @description 6752 * Name of the module. 6753 */ 6754 name: name, 6755 6756 6757 /** 6758 * @ngdoc method 6759 * @name angular.Module#provider 6760 * @module ng 6761 * @param {string} name service name 6762 * @param {Function} providerType Construction function for creating new instance of the 6763 * service. 6764 * @description 6765 * See {@link auto.$provide#provider $provide.provider()}. 6766 */ 6767 provider: invokeLater('$provide', 'provider'), 6768 6769 /** 6770 * @ngdoc method 6771 * @name angular.Module#factory 6772 * @module ng 6773 * @param {string} name service name 6774 * @param {Function} providerFunction Function for creating new instance of the service. 6775 * @description 6776 * See {@link auto.$provide#factory $provide.factory()}. 6777 */ 6778 factory: invokeLater('$provide', 'factory'), 6779 6780 /** 6781 * @ngdoc method 6782 * @name angular.Module#service 6783 * @module ng 6784 * @param {string} name service name 6785 * @param {Function} constructor A constructor function that will be instantiated. 6786 * @description 6787 * See {@link auto.$provide#service $provide.service()}. 6788 */ 6789 service: invokeLater('$provide', 'service'), 6790 6791 /** 6792 * @ngdoc method 6793 * @name angular.Module#value 6794 * @module ng 6795 * @param {string} name service name 6796 * @param {*} object Service instance object. 6797 * @description 6798 * See {@link auto.$provide#value $provide.value()}. 6799 */ 6800 value: invokeLater('$provide', 'value'), 6801 6802 /** 6803 * @ngdoc method 6804 * @name angular.Module#constant 6805 * @module ng 6806 * @param {string} name constant name 6807 * @param {*} object Constant value. 6808 * @description 6809 * Because the constant are fixed, they get applied before other provide methods. 6810 * See {@link auto.$provide#constant $provide.constant()}. 6811 */ 6812 constant: invokeLater('$provide', 'constant', 'unshift'), 6813 6814 /** 6815 * @ngdoc method 6816 * @name angular.Module#animation 6817 * @module ng 6818 * @param {string} name animation name 6819 * @param {Function} animationFactory Factory function for creating new instance of an 6820 * animation. 6821 * @description 6822 * 6823 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. 6824 * 6825 * 6826 * Defines an animation hook that can be later used with 6827 * {@link ngAnimate.$animate $animate} service and directives that use this service. 6828 * 6829 * ```js 6830 * module.animation('.animation-name', function($inject1, $inject2) { 6831 * return { 6832 * eventName : function(element, done) { 6833 * //code to run the animation 6834 * //once complete, then run done() 6835 * return function cancellationFunction(element) { 6836 * //code to cancel the animation 6837 * } 6838 * } 6839 * } 6840 * }) 6841 * ``` 6842 * 6843 * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and 6844 * {@link ngAnimate ngAnimate module} for more information. 6845 */ 6846 animation: invokeLater('$animateProvider', 'register'), 6847 6848 /** 6849 * @ngdoc method 6850 * @name angular.Module#filter 6851 * @module ng 6852 * @param {string} name Filter name. 6853 * @param {Function} filterFactory Factory function for creating new instance of filter.
6854 * @description 6855 * See {@link ng.$filterProvider#register $filterProvider.register()}. 6856 */ 6857 filter: invokeLater('$filterProvider', 'register'), 6858 6859 /** 6860 * @ngdoc method 6861 * @name angular.Module#controller 6862 * @module ng 6863 * @param {string|Object} name Controller name, or an object map of controllers where the 6864 * keys are the names and the values are the constructors. 6865 * @param {Function} constructor Controller constructor function. 6866 * @description 6867 * See {@link ng.$controllerProvider#register $controllerProvider.register()}. 6868 */ 6869 controller: invokeLater('$controllerProvider', 'register'), 6870 6871 /** 6872 * @ngdoc method 6873 * @name angular.Module#directive 6874 * @module ng 6875 * @param {string|Object} name Directive name, or an object map of directives where the 6876 * keys are the names and the values are the factories. 6877 * @param {Function} directiveFactory Factory function for creating new instance of 6878 * directives. 6879 * @description 6880 * See {@link ng.$compileProvider#directive $compileProvider.directive()}. 6881 */ 6882 directive: invokeLater('$compileProvider', 'directive'), 6883 6884 /** 6885 * @ngdoc method 6886 * @name angular.Module#config 6887 * @module ng 6888 * @param {Function} configFn Execute this function on module load. Useful for service 6889 * configuration. 6890 * @description 6891 * Use this method to register work which needs to be performed on module loading. 6892 * For more about how to configure services, see 6893 * {@link providers#providers_provider-recipe Provider Recipe}. 6894 */ 6895 config: config, 6896 6897 /** 6898 * @ngdoc method 6899 * @name angular.Module#run 6900 * @module ng 6901 * @param {Function} initializationFn Execute this function after injector creation. 6902 * Useful for application initialization. 6903 * @description 6904 * Use this method to register work which should be performed when the injector is done 6905 * loading all modules. 6906 */ 6907 run: function(block) { 6908 runBlocks.push(block); 6909 return this; 6910 } 6911 }; 6912 6913 if (configFn) { 6914 config(configFn); 6915 } 6916 6917 return moduleInstance; 6918 6919 /** 6920 * @param {string} provider 6921 * @param {string} method 6922 * @param {String=} insertMethod 6923 * @returns {angular.Module} 6924 */ 6925 function invokeLater(provider, method, insertMethod) { 6926 return function() { 6927 invokeQueue[insertMethod || 'push']([provider, method, arguments]); 6928 return moduleInstance; 6929 }; 6930 } 6931 }); 6932 }; 6933 }); 6934 6935} 6936 6937/* global angularModule: true, 6938 version: true, 6939 6940 $LocaleProvider, 6941 $CompileProvider, 6942 6943 htmlAnchorDirective, 6944 inputDirective, 6945 inputDirective, 6946 formDirective, 6947 scriptDirective, 6948 selectDirective, 6949 styleDirective, 6950 optionDirective, 6951 ngBindDirective, 6952 ngBindHtmlDirective, 6953 ngBindTemplateDirective, 6954 ngClassDirective, 6955 ngClassEvenDirective, 6956 ngClassOddDirective, 6957 ngCspDirective, 6958 ngCloakDirective, 6959 ngControllerDirective, 6960 ngFormDirective, 6961 ngHideDirective, 6962 ngIfDirective, 6963 ngIncludeDirective, 6964 ngIncludeFillContentDirective, 6965 ngInitDirective, 6966 ngNonBindableDirective, 6967 ngPluralizeDirective, 6968 ngRepeatDirective, 6969 ngShowDirective, 6970 ngStyleDirective, 6971 ngSwitchDirective, 6972 ngSwitchWhenDirective, 6973 ngSwitchDefaultDirective, 6974 ngOptionsDirective, 6975 ngTranscludeDirective, 6976 ngModelDirective, 6977 ngListDirective, 6978 ngChangeDirective, 6979 requiredDirective, 6980 requiredDirective, 6981 ngValueDirective, 6982 ngAttributeAliasDirectives, 6983 ngEventDirectives, 6984 6985 $AnchorScrollProvider, 6986 $AnimateProvider, 6987 $BrowserProvider,
6988 $CacheFactoryProvider, 6989 $ControllerProvider, 6990 $DocumentProvider, 6991 $ExceptionHandlerProvider, 6992 $FilterProvider, 6993 $InterpolateProvider, 6994 $IntervalProvider, 6995 $HttpProvider, 6996 $HttpBackendProvider, 6997 $LocationProvider, 6998 $LogProvider, 6999 $ParseProvider, 7000 $RootScopeProvider, 7001 $QProvider, 7002 $$SanitizeUriProvider, 7003 $SceProvider, 7004 $SceDelegateProvider, 7005 $SnifferProvider, 7006 $TemplateCacheProvider, 7007 $TimeoutProvider, 7008 $$RAFProvider, 7009 $$AsyncCallbackProvider, 7010 $WindowProvider 7011*/ 7012 7013 7014/** 7015 * @ngdoc object 7016 * @name angular.version 7017 * @module ng 7018 * @description 7019 * An object that contains information about the current AngularJS version. This object has the 7020 * following properties: 7021 * 7022 * - `full` â `{string}` â Full version string, such as "0.9.18". 7023 * - `major` â `{number}` â Major version number, such as "0". 7024 * - `minor` â `{number}` â Minor version number, such as "9". 7025 * - `dot` â `{number}` â Dot version number, such as "18". 7026 * - `codeName` â `{string}` â Code name of the release, such as "jiggling-armfat". 7027 */ 7028var version = { 7029 full: '1.2.25', // all of these placeholder strings will be replaced by grunt's 7030 major: 1, // package task 7031 minor: 2, 7032 dot: 25, 7033 codeName: 'hypnotic-gesticulation' 7034}; 7035 7036 7037function publishExternalAPI(angular){ 7038 extend(angular, { 7039 'bootstrap': bootstrap, 7040 'copy': copy, 7041 'extend': extend, 7042 'equals': equals, 7043 'element': jqLite, 7044 'forEach': forEach, 7045 'injector': createInjector, 7046 'noop': noop, 7047 'bind': bind, 7048 'toJson': toJson, 7049 'fromJson': fromJson, 7050 'identity': identity, 7051 'isUndefined': isUndefined, 7052 'isDefined': isDefined, 7053 'isString': isString, 7054 'isFunction': isFunction, 7055 'isObject': isObject, 7056 'isNumber': isNumber, 7057 'isElement': isElement, 7058 'isArray': isArray, 7059 'version': version, 7060 'isDate': isDate, 7061 'lowercase': lowercase, 7062 'uppercase': uppercase, 7063 'callbacks': {counter: 0}, 7064 '$$minErr': minErr, 7065 '$$csp': csp 7066 }); 7067 7068 angularModule = setupModuleLoader(window); 7069 try { 7070 angularModule('ngLocale'); 7071 } catch (e) { 7072 angularModule('ngLocale', []).provider('$locale', $LocaleProvider); 7073 } 7074 7075 angularModule('ng', ['ngLocale'], ['$provide', 7076 function ngModule($provide) { 7077 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. 7078 $provide.provider({ 7079 $$sanitizeUri: $$SanitizeUriProvider 7080 }); 7081 $provide.provider('$compile', $CompileProvider). 7082 directive({ 7083 a: htmlAnchorDirective, 7084 input: inputDirective, 7085 textarea: inputDirective, 7086 form: formDirective, 7087 script: scriptDirective, 7088 select: selectDirective, 7089 style: styleDirective, 7090 option: optionDirective, 7091 ngBind: ngBindDirective, 7092 ngBindHtml: ngBindHtmlDirective, 7093 ngBindTemplate: ngBindTemplateDirective, 7094 ngClass: ngClassDirective, 7095 ngClassEven: ngClassEvenDirective, 7096 ngClassOdd: ngClassOddDirective, 7097 ngCloak: ngCloakDirective, 7098 ngController: ngControllerDirective, 7099 ngForm: ngFormDirective, 7100 ngHide: ngHideDirective, 7101 ngIf: ngIfDirective, 7102 ngInclude: ngIncludeDirective, 7103 ngInit: ngInitDirective, 7104 ngNonBindable: ngNonBindableDirective, 7105 ngPluralize: ngPluralizeDirective, 7106 ngRepeat: ngRepeatDirective, 7107 ngShow: ngShowDirective, 7108 ngStyle: ngStyleDirective, 7109 ngSwitch: ngSwitchDirective, 7110 ngSwitchWhen: ngSwitchWhenDirective, 7111 ngSwitchDefault: ngSwitchDefaultDirective, 7112 ngOptions: ngOptionsDirective, 7113 ngTransclude: ngTranscludeDirective, 7114 ngModel: ngModelDirective, 7115 ngList: ngListDirective, 7116 ngChange: ngChangeDirective, 7117 required: requiredDirective, 7118 ngRequired: requiredDirective, 7119 ngValue: ngValueDirective 7120 }). 7121 directive({ 7122 ngInclude: ngIncludeFillContentDirective 7123 }). 7124 directive(ngAttributeAliasDirectives). 7125 directive(ngEventDirectives); 7126 $provide.provider({ 7127 $anchorScroll: $AnchorScrollProvider, 7128 $animate: $AnimateProvider, 7129 $browser: $BrowserProvider,
7130 $cacheFactory: $CacheFactoryProvider, 7131 $controller: $ControllerProvider, 7132 $document: $DocumentProvider, 7133 $exceptionHandler: $ExceptionHandlerProvider, 7134 $filter: $FilterProvider, 7135 $interpolate: $InterpolateProvider, 7136 $interval: $IntervalProvider, 7137 $http: $HttpProvider, 7138 $httpBackend: $HttpBackendProvider, 7139 $location: $LocationProvider, 7140 $log: $LogProvider, 7141 $parse: $ParseProvider, 7142 $rootScope: $RootScopeProvider, 7143 $q: $QProvider, 7144 $sce: $SceProvider, 7145 $sceDelegate: $SceDelegateProvider, 7146 $sniffer: $SnifferProvider, 7147 $templateCache: $TemplateCacheProvider, 7148 $timeout: $TimeoutProvider, 7149 $window: $WindowProvider, 7150 $$rAF: $$RAFProvider, 7151 $$asyncCallback : $$AsyncCallbackProvider 7152 }); 7153 } 7154 ]); 7155} 7156 7157/* global JQLitePrototype: true, 7158 addEventListenerFn: true, 7159 removeEventListenerFn: true, 7160 BOOLEAN_ATTR: true 7161*/ 7162 7163////////////////////////////////// 7164//JQLite 7165////////////////////////////////// 7166 7167/** 7168 * @ngdoc function 7169 * @name angular.element 7170 * @module ng 7171 * @kind function 7172 * 7173 * @description 7174 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. 7175 * 7176 * If jQuery is available, `angular.element` is an alias for the 7177 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` 7178 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." 7179 * 7180 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows 7181 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most 7182 * commonly needed functionality with the goal of having a very small footprint.</div> 7183 * 7184 * To use jQuery, simply load it before `DOMContentLoaded` event fired. 7185 * 7186 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or 7187 * jqLite; they are never raw DOM references.</div> 7188 * 7189 * ## Angular's jqLite 7190 * jqLite provides only the following jQuery methods: 7191 * 7192 * - [`addClass()`](http://api.jquery.com/addClass/) 7193 * - [`after()`](http://api.jquery.com/after/) 7194 * - [`append()`](http://api.jquery.com/append/) 7195 * - [`attr()`](http://api.jquery.com/attr/) 7196 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData 7197 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors 7198 * - [`clone()`](http://api.jquery.com/clone/) 7199 * - [`contents()`](http://api.jquery.com/contents/) 7200 * - [`css()`](http://api.jquery.com/css/) 7201 * - [`data()`](http://api.jquery.com/data/) 7202 * - [`empty()`](http://api.jquery.com/empty/) 7203 * - [`eq()`](http://api.jquery.com/eq/) 7204 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name 7205 * - [`hasClass()`](http://api.jquery.com/hasClass/) 7206 * - [`html()`](http://api.jquery.com/html/) 7207 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors 7208 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData 7209 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors 7210 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors 7211 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors 7212 * - [`prepend()`](http://api.jquery.com/prepend/) 7213 * - [`prop()`](http://api.jquery.com/prop/) 7214 * - [`ready()`](http://api.jquery.com/ready/) 7215 * - [`remove()`](http://api.jquery.com/remove/) 7216 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) 7217 * - [`removeClass()`](http://api.jquery.com/removeClass/) 7218 * - [`removeData()`](http://api.jquery.com/removeData/) 7219 * - [`replaceWith()`](http://api.jquery.com/replaceWith/) 7220 * - [`text()`](http://api.jquery.com/text/) 7221 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) 7222 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. 7223 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces 7224 * - [`val()`](http://api.jquery.com/val/) 7225 * - [`wrap()`](http://api.jquery.com/wrap/) 7226 * 7227 * ## jQuery/jqLite Extras 7228 * Angular also provides the following additional methods and events to both jQuery and jqLite: 7229 * 7230 * ### Events 7231 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
7231pis and fires this event 7232 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM 7233 * element before it is removed. 7234 * 7235 * ### Methods 7236 * - `controller(name)` - retrieves the controller of the current element or its parent. By default 7237 * retrieves controller associated with the `ngController` directive. If `name` is provided as 7238 * camelCase directive name, then the controller for this directive will be retrieved (e.g. 7239 * `'ngModel'`). 7240 * - `injector()` - retrieves the injector of the current element or its parent. 7241 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current 7242 * element or its parent. 7243 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the 7244 * current element. This getter should be used only on elements that contain a directive which starts a new isolate 7245 * scope. Calling `scope()` on this element always returns the original non-isolate scope. 7246 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top 7247 * parent element is reached. 7248 * 7249 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. 7250 * @returns {Object} jQuery object. 7251 */ 7252 7253JQLite.expando = 'ng339'; 7254 7255var jqCache = JQLite.cache = {}, 7256 jqId = 1, 7257 addEventListenerFn = (window.document.addEventListener 7258 ? function(element, type, fn) {element.addEventListener(type, fn, false);} 7259 : function(element, type, fn) {element.attachEvent('on' + type, fn);}), 7260 removeEventListenerFn = (window.document.removeEventListener 7261 ? function(element, type, fn) {element.removeEventListener(type, fn, false); } 7262 : function(element, type, fn) {element.detachEvent('on' + type, fn); }); 7263 7264/* 7265 * !!! This is an undocumented "private" function !!! 7266 */ 7267var jqData = JQLite._data = function(node) { 7268 //jQuery always returns an object on cache miss 7269 return this.cache[node[this.expando]] || {}; 7270}; 7271 7272function jqNextId() { return ++jqId; } 7273 7274 7275var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; 7276var MOZ_HACK_REGEXP = /^moz([A-Z])/; 7277var jqLiteMinErr = minErr('jqLite'); 7278 7279/** 7280 * Converts snake_case to camelCase. 7281 * Also there is special case for Moz prefix starting with upper case letter. 7282 * @param name Name to normalize 7283 */ 7284function camelCase(name) { 7285 return name. 7286 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { 7287 return offset ? letter.toUpperCase() : letter; 7288 }). 7289 replace(MOZ_HACK_REGEXP, 'Moz$1'); 7290} 7291 7292///////////////////////////////////////////// 7293// jQuery mutation patch 7294// 7295// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a 7296// $destroy event on all DOM nodes being removed. 7297// 7298///////////////////////////////////////////// 7299 7300function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) { 7301 var originalJqFn = jQuery.fn[name]; 7302 originalJqFn = originalJqFn.$original || originalJqFn; 7303 removePatch.$original = originalJqFn; 7304 jQuery.fn[name] = removePatch; 7305 7306 function removePatch(param) { 7307 // jshint -W040 7308 var list = filterElems && param ? [this.filter(param)] : [this], 7309 fireEvent = dispatchThis, 7310 set, setIndex, setLength, 7311 element, childIndex, childLength, children; 7312 7313 if (!getterIfNoArguments || param != null) { 7314 while(list.length) { 7315 set = list.shift(); 7316 for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { 7317 element = jqLite(set[setIndex]); 7318 if (fireEvent) { 7319 element.triggerHandler('$destroy'); 7320 } else { 7321 fireEvent = !fireEvent; 7322 } 7323 for(childIndex = 0, childLength = (children = element.children()).length; 7324 childIndex < childLength; 7325 childIndex++) { 7326 list.push(jQuery(children[childIndex])); 7327 } 7328 } 7329 } 7330 } 7331 return originalJqFn.apply(this, arguments); 7332 } 7333} 7334 7335var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
7336var HTML_REGEXP = /<|&#?\w+;/; 7337var TAG_NAME_REGEXP = /<([\w:]+)/; 7338var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; 7339 7340var wrapMap = { 7341 'option': [1, '<select multiple="multiple">', '</select>'], 7342 7343 'thead': [1, '<table>', '</table>'], 7344 'col': [2, '<table><colgroup>', '</colgroup></table>'], 7345 'tr': [2, '<table><tbody>', '</tbody></table>'], 7346 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], 7347 '_default': [0, "", ""] 7348}; 7349 7350wrapMap.optgroup = wrapMap.option; 7351wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 7352wrapMap.th = wrapMap.td; 7353 7354function jqLiteIsTextNode(html) { 7355 return !HTML_REGEXP.test(html); 7356} 7357 7358function jqLiteBuildFragment(html, context) { 7359 var elem, tmp, tag, wrap, 7360 fragment = context.createDocumentFragment(), 7361 nodes = [], i, j, jj; 7362 7363 if (jqLiteIsTextNode(html)) { 7364 // Convert non-html into a text node 7365 nodes.push(context.createTextNode(html)); 7366 } else { 7367 tmp = fragment.appendChild(context.createElement('div')); 7368 // Convert html into DOM nodes 7369 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); 7370 wrap = wrapMap[tag] || wrapMap._default; 7371 tmp.innerHTML = '<div> </div>' + 7372 wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2]; 7373 tmp.removeChild(tmp.firstChild); 7374 7375 // Descend through wrappers to the right content 7376 i = wrap[0]; 7377 while (i--) { 7378 tmp = tmp.lastChild; 7379 } 7380 7381 for (j=0, jj=tmp.childNodes.length; j<jj; ++j) nodes.push(tmp.childNodes[j]); 7382 7383 tmp = fragment.firstChild; 7384 tmp.textContent = ""; 7385 } 7386 7387 // Remove wrapper from fragment 7388 fragment.textContent = ""; 7389 fragment.innerHTML = ""; // Clear inner HTML 7390 return nodes; 7391} 7392 7393function jqLiteParseHTML(html, context) { 7394 context = context || document; 7395 var parsed; 7396 7397 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { 7398 return [context.createElement(parsed[1])]; 7399 } 7400 7401 return jqLiteBuildFragment(html, context); 7402} 7403 7404///////////////////////////////////////////// 7405function JQLite(element) { 7406 if (element instanceof JQLite) { 7407 return element; 7408 } 7409 if (isString(element)) { 7410 element = trim(element); 7411 } 7412 if (!(this instanceof JQLite)) { 7413 if (isString(element) && element.charAt(0) != '<') { 7414 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); 7415 } 7416 return new JQLite(element); 7417 } 7418 7419 if (isString(element)) { 7420 jqLiteAddNodes(this, jqLiteParseHTML(element)); 7421 var fragment = jqLite(document.createDocumentFragment()); 7422 fragment.append(this); 7423 } else { 7424 jqLiteAddNodes(this, element); 7425 } 7426} 7427 7428function jqLiteClone(element) { 7429 return element.cloneNode(true); 7430} 7431 7432function jqLiteDealoc(element){ 7433 jqLiteRemoveData(element); 7434 for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { 7435 jqLiteDealoc(children[i]); 7436 } 7437} 7438 7439function jqLiteOff(element, type, fn, unsupported) {
7440 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); 7441 7442 var events = jqLiteExpandoStore(element, 'events'), 7443 handle = jqLiteExpandoStore(element, 'handle'); 7444 7445 if (!handle) return; //no listeners registered 7446 7447 if (isUndefined(type)) { 7448 forEach(events, function(eventHandler, type) { 7449 removeEventListenerFn(element, type, eventHandler); 7450 delete events[type]; 7451 }); 7452 } else { 7453 forEach(type.split(' '), function(type) { 7454 if (isUndefined(fn)) { 7455 removeEventListenerFn(element, type, events[type]); 7456 delete events[type]; 7457 } else { 7458 arrayRemove(events[type] || [], fn); 7459 } 7460 }); 7461 } 7462} 7463 7464function jqLiteRemoveData(element, name) { 7465 var expandoId = element.ng339, 7466 expandoStore = jqCache[expandoId]; 7467 7468 if (expandoStore) { 7469 if (name) { 7470 delete jqCache[expandoId].data[name]; 7471 return; 7472 } 7473 7474 if (expandoStore.handle) { 7475 expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); 7476 jqLiteOff(element); 7477 } 7478 delete jqCache[expandoId]; 7479 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it 7480 } 7481} 7482 7483function jqLiteExpandoStore(element, key, value) { 7484 var expandoId = element.ng339, 7485 expandoStore = jqCache[expandoId || -1]; 7486 7487 if (isDefined(value)) { 7488 if (!expandoStore) { 7489 element.ng339 = expandoId = jqNextId(); 7490 expandoStore = jqCache[expandoId] = {}; 7491 } 7492 expandoStore[key] = value; 7493 } else { 7494 return expandoStore && expandoStore[key]; 7495 } 7496} 7497 7498function jqLiteData(element, key, value) { 7499 var data = jqLiteExpandoStore(element, 'data'), 7500 isSetter = isDefined(value), 7501 keyDefined = !isSetter && isDefined(key), 7502 isSimpleGetter = keyDefined && !isObject(key); 7503 7504 if (!data && !isSimpleGetter) { 7505 jqLiteExpandoStore(element, 'data', data = {}); 7506 } 7507 7508 if (isSetter) { 7509 data[key] = value; 7510 } else { 7511 if (keyDefined) { 7512 if (isSimpleGetter) { 7513 // don't create data in this case. 7514 return data && data[key]; 7515 } else { 7516 extend(data, key); 7517 } 7518 } else { 7519 return data; 7520 } 7521 } 7522} 7523 7524function jqLiteHasClass(element, selector) { 7525 if (!element.getAttribute) return false; 7526 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). 7527 indexOf( " " + selector + " " ) > -1); 7528} 7529 7530function jqLiteRemoveClass(element, cssClasses) { 7531 if (cssClasses && element.setAttribute) { 7532 forEach(cssClasses.split(' '), function(cssClass) { 7533 element.setAttribute('class', trim( 7534 (" " + (element.getAttribute('class') || '') + " ") 7535 .replace(/[\n\t]/g, " ") 7536 .replace(" " + trim(cssClass) + " ", " ")) 7537 ); 7538 }); 7539 } 7540} 7541 7542function jqLiteAddClass(element, cssClasses) { 7543 if (cssClasses && element.setAttribute) { 7544 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') 7545 .replace(/[\n\t]/g, " "); 7546 7547 forEach(cssClasses.split(' '), function(cssClass) { 7548 cssClass = trim(cssClass); 7549 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { 7550 existingClasses += cssClass + ' '; 7551 } 7552 }); 7553 7554 element.setAttribute('class', trim(existingClasses)); 7555 } 7556} 7557 7558function jqLiteAddNodes(root, elements) { 7559 if (elements) { 7560 elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) 7561 ? elements 7562 : [ elements ]; 7563 for(var i=0; i < elements.length; i++) { 7564 root.push(elements[i]); 7565 } 7566 } 7567} 7568 7569function jqLiteController(element, name) { 7570 return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); 7571} 7572 7573function jqLiteInheritedData(element, name, value) { 7574 // if element is the document object work with the html element instead 7575 // this makes $(document).scope() possible 7576 if(element.nodeType == 9) { 7577 element = element.documentElement; 7578 } 7579 var names = isArray(name) ? name : [name]; 7580 7581 while (element) { 7582 for (var i = 0, ii = names.length; i < ii; i++) { 7583 if ((value = jqLite.data(element, names[i])) !== undefined) return value; 7584 } 7585 7586 // If dealing with a document fragment node with a host element, and no parent, use the host 7587 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM 7588 // to lookup parent controllers. 7589 element = element.parentNode || (element.nodeType === 11 && element.host); 7590 } 7591} 7592 7593function jqLiteEmpty(element) { 7594 for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { 7595 jqLiteDealoc(childNodes[i]); 7596 } 7597 while (element.firstChild) { 7598 element.removeChild(element.firstChild); 7599 } 7600} 7601 7602////////////////////////////////////////// 7603// Functions which are declared directly.
7604////////////////////////////////////////// 7605var JQLitePrototype = JQLite.prototype = { 7606 ready: function(fn) { 7607 var fired = false; 7608 7609 function trigger() { 7610 if (fired) return; 7611 fired = true; 7612 fn(); 7613 } 7614 7615 // check if document already is loaded 7616 if (document.readyState === 'complete'){ 7617 setTimeout(trigger); 7618 } else { 7619 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 7620 // we can not use jqLite since we are not done loading and jQuery could be loaded later. 7621 // jshint -W064 7622 JQLite(window).on('load', trigger); // fallback to window.onload for others 7623 // jshint +W064 7624 } 7625 }, 7626 toString: function() { 7627 var value = []; 7628 forEach(this, function(e){ value.push('' + e);}); 7629 return '[' + value.join(', ') + ']'; 7630 }, 7631 7632 eq: function(index) { 7633 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); 7634 }, 7635 7636 length: 0, 7637 push: push, 7638 sort: [].sort, 7639 splice: [].splice 7640}; 7641 7642////////////////////////////////////////// 7643// Functions iterating getter/setters. 7644// these functions return self on setter and 7645// value on get. 7646////////////////////////////////////////// 7647var BOOLEAN_ATTR = {}; 7648forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { 7649 BOOLEAN_ATTR[lowercase(value)] = value; 7650}); 7651var BOOLEAN_ELEMENTS = {}; 7652forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { 7653 BOOLEAN_ELEMENTS[uppercase(value)] = true; 7654}); 7655 7656function getBooleanAttrName(element, name) { 7657 // check dom last since we will most likely fail on name 7658 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; 7659 7660 // booleanAttr is here twice to minimize DOM access 7661 return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; 7662} 7663 7664forEach({ 7665 data: jqLiteData, 7666 removeData: jqLiteRemoveData 7667}, function(fn, name) { 7668 JQLite[name] = fn; 7669}); 7670 7671forEach({ 7672 data: jqLiteData, 7673 inheritedData: jqLiteInheritedData, 7674 7675 scope: function(element) { 7676 // Can't use jqLiteData here directly so we stay compatible with jQuery! 7677 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); 7678 }, 7679 7680 isolateScope: function(element) { 7681 // Can't use jqLiteData here directly so we stay compatible with jQuery! 7682 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); 7683 }, 7684 7685 controller: jqLiteController, 7686 7687 injector: function(element) { 7688 return jqLiteInheritedData(element, '$injector'); 7689 }, 7690 7691 removeAttr: function(element,name) { 7692 element.removeAttribute(name); 7693 }, 7694 7695 hasClass: jqLiteHasClass, 7696 7697 css: function(element, name, value) { 7698 name = camelCase(name); 7699 7700 if (isDefined(value)) { 7701 element.style[name] = value; 7702 } else { 7703 var val; 7704 7705 if (msie <= 8) { 7706 // this is some IE specific weirdness that jQuery 1.6.4 does not sure why 7707 val = element.currentStyle && element.currentStyle[name]; 7708 if (val === '') val = 'auto'; 7709 } 7710 7711 val = val || element.style[name]; 7712 7713 if (msie <= 8) { 7714 // jquery weirdness :-/ 7715 val = (val === '') ? undefined : val; 7716 } 7717 7718 return val; 7719 } 7720 }, 7721 7722 attr: function(element, name, value){ 7723 var lowercasedName = lowercase(name); 7724 if (BOOLEAN_ATTR[lowercasedName]) { 7725 if (isDefined(value)) { 7726 if (!!value) { 7727 element[name] = true; 7728 element.setAttribute(name, lowercasedName); 7729 } else { 7730 element[name] = false; 7731 element.removeAttribute(lowercasedName); 7732 } 7733 } else { 7734 return (element[name] || 7735 (element.attributes.getNamedItem(name)|| noop).specified) 7736 ? lowercasedName 7737 : undefined; 7738 } 7739 } else if (isDefined(value)) { 7740 element.setAttribute(name, value); 7741 } else if (element.getAttribute) { 7742 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code 7743 // some elements (e.g. Document) don't have get attribute, so return undefined 7744 var ret = element.getAttribute(name, 2); 7745 // normalize non-existing attributes to undefined (as jQuery) 7746 return ret === null ? undefined : ret; 7747 } 7748 }, 7749 7750 prop: function(element, name, value) { 7751 if (isDefined(value)) { 7752 element[name] = value; 7753 } else { 7754 return element[name]; 7755 } 7756 }, 7757 7758 text: (function() { 7759 var NODE_TYPE_TEXT_PROPERTY = []; 7760 if (msie < 9) { 7761 NODE_TYPE_TEXT_PROPERTY[1] = 'innerText'; /** Element **/ 7762 NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue'; /** Text **/ 7763 } else { 7764 NODE_TYPE_TEXT_PROPERTY[1] = /** Element **/ 7765 NODE_TYPE_TEXT_PROPERTY[3] = 'textContent'; /** Text **/ 7766 } 7767 getText.$dv = ''; 7768 return getText; 7769 7770 function getText(element, value) { 7771 var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType]; 7772 if (isUndefined(value)) { 7773 return textProp ? element[textProp] : ''; 7774 } 7775 element[textProp] = value; 7776 } 7777 })(), 7778 7779 val: function(element, value) { 7780 if (isUndefined(value)) { 7781 if (nodeName_(element) === 'SELECT' && element.multiple) { 7782 var result = [];
7783 forEach(element.options, function (option) { 7784 if (option.selected) { 7785 result.push(option.value || option.text); 7786 } 7787 }); 7788 return result.length === 0 ? null : result; 7789 } 7790 return element.value; 7791 } 7792 element.value = value; 7793 }, 7794 7795 html: function(element, value) { 7796 if (isUndefined(value)) { 7797 return element.innerHTML; 7798 } 7799 for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { 7800 jqLiteDealoc(childNodes[i]); 7801 } 7802 element.innerHTML = value; 7803 }, 7804 7805 empty: jqLiteEmpty 7806}, function(fn, name){ 7807 /** 7808 * Properties: writes return selection, reads return first value 7809 */ 7810 JQLite.prototype[name] = function(arg1, arg2) { 7811 var i, key; 7812 var nodeCount = this.length; 7813 7814 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it 7815 // in a way that survives minification. 7816 // jqLiteEmpty takes no arguments but is a setter. 7817 if (fn !== jqLiteEmpty && 7818 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { 7819 if (isObject(arg1)) { 7820 7821 // we are a write, but the object properties are the key/values 7822 for (i = 0; i < nodeCount; i++) { 7823 if (fn === jqLiteData) { 7824 // data() takes the whole object in jQuery 7825 fn(this[i], arg1); 7826 } else { 7827 for (key in arg1) { 7828 fn(this[i], key, arg1[key]); 7829 } 7830 } 7831 } 7832 // return self for chaining 7833 return this; 7834 } else { 7835 // we are a read, so read the first child. 7836 // TODO: do we still need this? 7837 var value = fn.$dv; 7838 // Only if we have $dv do we iterate over all, otherwise it is just the first element. 7839 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; 7840 for (var j = 0; j < jj; j++) { 7841 var nodeValue = fn(this[j], arg1, arg2); 7842 value = value ? value + nodeValue : nodeValue; 7843 } 7844 return value; 7845 } 7846 } else { 7847 // we are a write, so apply to all children 7848 for (i = 0; i < nodeCount; i++) { 7849 fn(this[i], arg1, arg2); 7850 } 7851 // return self for chaining 7852 return this; 7853 } 7854 }; 7855}); 7856 7857function createEventHandler(element, events) { 7858 var eventHandler = function (event, type) { 7859 if (!event.preventDefault) { 7860 event.preventDefault = function() { 7861 event.returnValue = false; //ie 7862 }; 7863 } 7864 7865 if (!event.stopPropagation) { 7866 event.stopPropagation = function() { 7867 event.cancelBubble = true; //ie 7868 }; 7869 } 7870 7871 if (!event.target) { 7872 event.target = event.srcElement || document; 7873 } 7874 7875 if (isUndefined(event.defaultPrevented)) { 7876 var prevent = event.preventDefault; 7877 event.preventDefault = function() { 7878 event.defaultPrevented = true; 7879 prevent.call(event); 7880 }; 7881 event.defaultPrevented = false; 7882 } 7883 7884 event.isDefaultPrevented = function() { 7885 return event.defaultPrevented || event.returnValue === false; 7886 }; 7887 7888 // Copy event handlers in case event handlers array is modified during execution. 7889 var eventHandlersCopy = shallowCopy(events[type || event.type] || []); 7890 7891 forEach(eventHandlersCopy, function(fn) { 7892 fn.call(element, event); 7893 }); 7894 7895 // Remove monkey-patched methods (IE), 7896 // as they would cause memory leaks in IE8. 7897 if (msie <= 8) { 7898 // IE7/8 does not allow to delete property on native object 7899 event.preventDefault = null; 7900 event.stopPropagation = null; 7901 event.isDefaultPrevented = null; 7902 } else { 7903 // It shouldn't affect normal browsers (native methods are defined on prototype). 7904 delete event.preventDefault; 7905 delete event.stopPropagation; 7906 delete event.isDefaultPrevented; 7907 } 7908 }; 7909 eventHandler.elem = element; 7910 return eventHandler; 7911} 7912 7913////////////////////////////////////////// 7914// Functions iterating traversal.
7915// These functions chain results into a single 7916// selector. 7917////////////////////////////////////////// 7918forEach({ 7919 removeData: jqLiteRemoveData, 7920 7921 dealoc: jqLiteDealoc, 7922 7923 on: function onFn(element, type, fn, unsupported){ 7924 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); 7925 7926 var events = jqLiteExpandoStore(element, 'events'), 7927 handle = jqLiteExpandoStore(element, 'handle'); 7928 7929 if (!events) jqLiteExpandoStore(element, 'events', events = {}); 7930 if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); 7931 7932 forEach(type.split(' '), function(type){ 7933 var eventFns = events[type]; 7934 7935 if (!eventFns) { 7936 if (type == 'mouseenter' || type == 'mouseleave') { 7937 var contains = document.body.contains || document.body.compareDocumentPosition ? 7938 function( a, b ) { 7939 // jshint bitwise: false 7940 var adown = a.nodeType === 9 ? a.documentElement : a, 7941 bup = b && b.parentNode; 7942 return a === bup || !!( bup && bup.nodeType === 1 && ( 7943 adown.contains ? 7944 adown.contains( bup ) : 7945 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 7946 )); 7947 } : 7948 function( a, b ) { 7949 if ( b ) { 7950 while ( (b = b.parentNode) ) { 7951 if ( b === a ) { 7952 return true; 7953 } 7954 } 7955 } 7956 return false; 7957 }; 7958 7959 events[type] = []; 7960 7961 // Refer to jQuery's implementation of mouseenter & mouseleave 7962 // Read about mouseenter and mouseleave: 7963 // http://www.quirksmode.org/js/events_mouse.html#link8 7964 var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}; 7965 7966 onFn(element, eventmap[type], function(event) { 7967 var target = this, related = event.relatedTarget; 7968 // For mousenter/leave call the handler if related is outside the target. 7969 // NB: No relatedTarget if the mouse left/entered the browser window 7970 if ( !related || (related !== target && !contains(target, related)) ){ 7971 handle(event, type); 7972 } 7973 }); 7974 7975 } else { 7976 addEventListenerFn(element, type, handle); 7977 events[type] = []; 7978 } 7979 eventFns = events[type]; 7980 } 7981 eventFns.push(fn); 7982 }); 7983 }, 7984 7985 off: jqLiteOff, 7986 7987 one: function(element, type, fn) { 7988 element = jqLite(element); 7989 7990 //add the listener twice so that when it is called 7991 //you can remove the original function and still be 7992 //able to call element.off(ev, fn) normally 7993 element.on(type, function onFn() { 7994 element.off(type, fn); 7995 element.off(type, onFn); 7996 }); 7997 element.on(type, fn); 7998 }, 7999 8000 replaceWith: function(element, replaceNode) { 8001 var index, parent = element.parentNode; 8002 jqLiteDealoc(element); 8003 forEach(new JQLite(replaceNode), function(node){ 8004 if (index) { 8005 parent.insertBefore(node, index.nextSibling); 8006 } else { 8007 parent.replaceChild(node, element); 8008 } 8009 index = node; 8010 }); 8011 }, 8012 8013 children: function(element) { 8014 var children = []; 8015 forEach(element.childNodes, function(element){ 8016 if (element.nodeType === 1) 8017 children.push(element); 8018 }); 8019 return children; 8020 }, 8021 8022 contents: function(element) { 8023 return element.contentDocument || element.childNodes || []; 8024 }, 8025 8026 append: function(element, node) { 8027 forEach(new JQLite(node), function(child){ 8028 if (element.nodeType === 1 || element.nodeType === 11) { 8029 element.appendChild(child); 8030 } 8031 }); 8032 }, 8033 8034 prepend: function(element, node) { 8035 if (element.nodeType === 1) { 8036 var index = element.firstChild; 8037 forEach(new JQLite(node), function(child){ 8038 element.insertBefore(child, index); 8039 }); 8040 } 8041 }, 8042 8043 wrap: function(element, wrapNode) { 8044 wrapNode = jqLite(wrapNode)[0]; 8045 var parent = element.parentNode; 8046 if (parent) { 8047 parent.replaceChild(wrapNode, element); 8048 } 8049 wrapNode.appendChild(element); 8050 }, 8051 8052 remove: function(element) { 8053 jqLiteDealoc(element); 8054 var parent = element.parentNode; 8055 if (parent) parent.removeChild(element); 8056 }, 8057 8058 after: function(element, newElement) { 8059 var index = element, parent = element.parentNode;
8060 forEach(new JQLite(newElement), function(node){ 8061 parent.insertBefore(node, index.nextSibling); 8062 index = node; 8063 }); 8064 }, 8065 8066 addClass: jqLiteAddClass, 8067 removeClass: jqLiteRemoveClass, 8068 8069 toggleClass: function(element, selector, condition) { 8070 if (selector) { 8071 forEach(selector.split(' '), function(className){ 8072 var classCondition = condition; 8073 if (isUndefined(classCondition)) { 8074 classCondition = !jqLiteHasClass(element, className); 8075 } 8076 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); 8077 }); 8078 } 8079 }, 8080 8081 parent: function(element) { 8082 var parent = element.parentNode; 8083 return parent && parent.nodeType !== 11 ? parent : null; 8084 }, 8085 8086 next: function(element) { 8087 if (element.nextElementSibling) { 8088 return element.nextElementSibling; 8089 } 8090 8091 // IE8 doesn't have nextElementSibling 8092 var elm = element.nextSibling; 8093 while (elm != null && elm.nodeType !== 1) { 8094 elm = elm.nextSibling; 8095 } 8096 return elm; 8097 }, 8098 8099 find: function(element, selector) { 8100 if (element.getElementsByTagName) { 8101 return element.getElementsByTagName(selector); 8102 } else { 8103 return []; 8104 } 8105 }, 8106 8107 clone: jqLiteClone, 8108 8109 triggerHandler: function(element, event, extraParameters) { 8110 8111 var dummyEvent, eventFnsCopy, handlerArgs; 8112 var eventName = event.type || event; 8113 var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName]; 8114 8115 if (eventFns) { 8116 8117 // Create a dummy event to pass to the handlers 8118 dummyEvent = { 8119 preventDefault: function() { this.defaultPrevented = true; }, 8120 isDefaultPrevented: function() { return this.defaultPrevented === true; }, 8121 stopPropagation: noop, 8122 type: eventName, 8123 target: element 8124 }; 8125 8126 // If a custom event was provided then extend our dummy event with it 8127 if (event.type) { 8128 dummyEvent = extend(dummyEvent, event); 8129 } 8130 8131 // Copy event handlers in case event handlers array is modified during execution. 8132 eventFnsCopy = shallowCopy(eventFns); 8133 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; 8134 8135 forEach(eventFnsCopy, function(fn) { 8136 fn.apply(element, handlerArgs); 8137 }); 8138 8139 } 8140 } 8141}, function(fn, name){ 8142 /** 8143 * chaining functions 8144 */ 8145 JQLite.prototype[name] = function(arg1, arg2, arg3) { 8146 var value; 8147 for(var i=0; i < this.length; i++) { 8148 if (isUndefined(value)) { 8149 value = fn(this[i], arg1, arg2, arg3); 8150 if (isDefined(value)) { 8151 // any function which returns a value needs to be wrapped 8152 value = jqLite(value); 8153 } 8154 } else { 8155 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); 8156 } 8157 } 8158 return isDefined(value) ? value : this; 8159 }; 8160 8161 // bind legacy bind/unbind to on/off 8162 JQLite.prototype.bind = JQLite.prototype.on; 8163 JQLite.prototype.unbind = JQLite.prototype.off; 8164}); 8165 8166/** 8167 * Computes a hash of an 'obj'. 8168 * Hash of a: 8169 * string is string 8170 * number is number as string 8171 * object is either result of calling $$hashKey function on the object or uniquely generated id, 8172 * that is also assigned to the $$hashKey property of the object. 8173 * 8174 * @param obj 8175 * @returns {string} hash string such that the same input will have the same hash string. 8176 * The resulting string key is in 'type:hashKey' format. 8177 */ 8178function hashKey(obj, nextUidFn) { 8179 var objType = typeof obj, 8180 key; 8181 8182 if (objType == 'function' || (objType == 'object' && obj !== null)) { 8183 if (typeof (key = obj.$$hashKey) == 'function') { 8184 // must invoke on object to keep the right this 8185 key = obj.$$hashKey(); 8186 } else if (key === undefined) { 8187 key = obj.$$hashKey = (nextUidFn || nextUid)(); 8188 } 8189 } else { 8190 key = obj; 8191 } 8192 8193 return objType + ':' + key; 8194} 8195 8196/** 8197 * HashMap which can use objects as keys 8198 */ 8199function HashMap(array, isolatedUid) { 8200 if (isolatedUid) { 8201 var uid = 0; 8202 this.nextUid = function() { 8203 return ++uid; 8204 }; 8205 } 8206 forEach(array, this.put, this); 8207} 8208HashMap.prototype = { 8209 /** 8210 * Store key value pair 8211 * @param key key to store can be any type 8212 * @param value value to store can be any type 8213 */ 8214 put: function(key, value) { 8215 this[hashKey(key, this.nextUid)] = value; 8216 }, 8217 8218 /** 8219 * @param key 8220 * @returns {Object} the value for the key 8221 */ 8222 get: function(key) { 8223 return this[hashKey(key, this.nextUid)]; 8224 }, 8225 8226 /** 8227 * Remove the key/value pair 8228 * @param key 8229 */ 8230 remove: function(key) { 8231 var value = this[key = hashKey(key, this.nextUid)]; 8232 delete this[key]; 8233 return value; 8234 } 8235}; 8236 8237/** 8238 * @ngdoc function 8239 * @module ng 8240 * @name angular.injector 8241 * @kind function 8242 * 8243 * @description 8244 * Creates an injector function that can be used for retrieving services as well as for 8245 * dependency injection (see {@link guide/di dependency injection}). 8246 * 8247 8248 * @param {Array.<string|Function>}
8248 modules A list of module functions or their aliases. See 8249 * {@link angular.module}. The `ng` module must be explicitly added. 8250 * @returns {function()} Injector function. See {@link auto.$injector $injector}. 8251 * 8252 * @example 8253 * Typical usage 8254 * ```js 8255 * // create an injector 8256 * var $injector = angular.injector(['ng']); 8257 * 8258 * // use the injector to kick off your application 8259 * // use the type inference to auto inject arguments, or use implicit injection 8260 * $injector.invoke(function($rootScope, $compile, $document){ 8261 * $compile($document)($rootScope); 8262 * $rootScope.$digest(); 8263 * }); 8264 * ``` 8265 * 8266 * Sometimes you want to get access to the injector of a currently running Angular app 8267 * from outside Angular. Perhaps, you want to inject and compile some markup after the 8268 * application has been bootstrapped. You can do this using the extra `injector()` added 8269 * to JQuery/jqLite elements. See {@link angular.element}. 8270 * 8271 * *This is fairly rare but could be the case if a third party library is injecting the 8272 * markup.* 8273 * 8274 * In the following example a new block of HTML containing a `ng-controller` 8275 * directive is added to the end of the document body by JQuery. We then compile and link 8276 * it into the current AngularJS scope. 8277 * 8278 * ```js 8279 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); 8280 * $(document.body).append($div); 8281 * 8282 * angular.element(document).injector().invoke(function($compile) { 8283 * var scope = angular.element($div).scope(); 8284 * $compile($div)(scope); 8285 * }); 8286 * ``` 8287 */ 8288 8289 8290/** 8291 * @ngdoc module 8292 * @name auto 8293 * @description 8294 * 8295 * Implicit module which gets automatically added to each {@link auto.$injector $injector}. 8296 */ 8297 8298var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; 8299var FN_ARG_SPLIT = /,/; 8300var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; 8301var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 8302var $injectorMinErr = minErr('$injector'); 8303function annotate(fn) { 8304 var $inject, 8305 fnText, 8306 argDecl, 8307 last; 8308 8309 if (typeof fn === 'function') { 8310 if (!($inject = fn.$inject)) { 8311 $inject = []; 8312 if (fn.length) { 8313 fnText = fn.toString().replace(STRIP_COMMENTS, ''); 8314 argDecl = fnText.match(FN_ARGS); 8315 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ 8316 arg.replace(FN_ARG, function(all, underscore, name){ 8317 $inject.push(name); 8318 }); 8319 }); 8320 } 8321 fn.$inject = $inject; 8322 } 8323 } else if (isArray(fn)) { 8324 last = fn.length - 1; 8325 assertArgFn(fn[last], 'fn'); 8326 $inject = fn.slice(0, last); 8327 } else { 8328 assertArgFn(fn, 'fn', true); 8329 } 8330 return $inject; 8331} 8332 8333/////////////////////////////////////// 8334 8335/** 8336 * @ngdoc service 8337 * @name $injector 8338 * @kind function 8339 * 8340 * @description 8341 * 8342 * `$injector` is used to retrieve object instances as defined by 8343 * {@link auto.$provide provider}, instantiate types, invoke methods, 8344 * and load modules. 8345 * 8346 * The following always holds true: 8347 * 8348 * ```js 8349 * var $injector = angular.injector(); 8350 * expect($injector.get('$injector')).toBe($injector); 8351 * expect($injector.invoke(function($injector){ 8352 * return $injector; 8353 * }).toBe($injector); 8354 * ``` 8355 * 8356 * # Injection Function Annotation 8357 * 8358 * JavaScript does not have annotations, and annotations are needed for dependency injection. The 8359 * following are all valid ways of annotating function with injection arguments and are equivalent. 8360 * 8361 * ```js 8362 * // inferred (only works if code not minified/obfuscated) 8363 * $injector.invoke(function(serviceA){}); 8364 * 8365 * // annotated 8366 * function explicit(serviceA) {}; 8367 * explicit.$inject = ['serviceA']; 8368 * $injector.invoke(explicit); 8369 * 8370 * // inline 8371 * $injector.invoke(['serviceA', function(serviceA){}]); 8372 * ``` 8373 * 8374 * ## Inference 8375 * 8376 * In JavaScript calling `toString()` on a function returns the function definition. The definition 8377 * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with 8378 * minification, and obfuscation tools since these tools change the argument names. 8379 * 8380 * ## `$inject` Annotation 8381 * By adding an `$inject` property onto a function the injection parameters can be specified. 8382 * 8383 * ## Inline 8384 * As an array of injection names, where the last item in the array is the function to call. 8385 */ 8386 8387/** 8388 * @ngdoc method 8389 * @name $injector#get 8390 * 8391 * @description 8392 * Return an instance of the service. 8393 * 8394 * @param {string} name The name of the instance to retrieve. 8395 * @return {*} The instance. 8396 */ 8397 8398/** 8399 * @ngdoc method 8400 * @name $injector#invoke 8401 * 8402 * @description 8403 * Invoke the method and supply the method arguments from the `$injector`. 8404 * 8405 * @param {!Function} fn The function to invoke. Function parameters are injected according to the 8406 * {@link guide/di $inject Annotation} rules. 8407 * @param {Object=} self The `this` for the invoked method. 8408 * @param {Object=} locals Optional object. If preset then any argument names are read from this 8409 * object first, before the `$injector` is consulted. 8410 * @returns {*} the value returned by the invoked `fn` function. 8411 */ 8412 8413/** 8414 * @ngdoc method 8415 * @name $injector#has 8416 * 8417 * @description 8418 * Allows the user to query if the particular service exists. 8419 * 8420 * @param {string} Name of the service to query. 8421 * @returns {boolean} returns true if injector has given service. 8422 */ 8423 8424/** 8425 * @ngdoc method 8426 * @name $injector#instantiate 8427 * @description 8428 * Create a new instance of JS type. The method takes a constructor function, invokes the new 8429 * operator, and supplies all of the arguments to the constructor function as specified by the 8430 * constructor annotation. 8431 * 8432 * @param {Function} Type Annotated constructor function. 8433 * @param {Object=} locals Optional object. If preset then any argument names are read from this 8434 * object first, before the `$injector` is consulted. 8435 * @returns {Object} new instance of `Type`. 8436 */ 8437 8438/** 8439 * @ngdoc method 8440 * @name $injector#annotate 8441 * 8442 * @description 8443 * Returns an array of service names which the function is requesting for injection. This API is 8444 * used by the injector to determine which services need to be injected into the function when the 8445 * function is invoked. There are three ways in which the function can be annotated with the needed 8446 * dependencies. 8447 * 8448 * # Argument names 8449 * 8450 * The simplest form is to extract the dependencies from the arguments of the function. This is done
8451 * by converting the function into a string using `toString()` method and extracting the argument 8452 * names. 8453 * ```js 8454 * // Given 8455 * function MyController($scope, $route) { 8456 * // ... 8457 * } 8458 * 8459 * // Then 8460 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 8461 * ``` 8462 * 8463 * This method does not work with code minification / obfuscation. For this reason the following 8464 * annotation strategies are supported. 8465 * 8466 * # The `$inject` property 8467 * 8468 * If a function has an `$inject` property and its value is an array of strings, then the strings 8469 * represent names of services to be injected into the function. 8470 * ```js 8471 * // Given 8472 * var MyController = function(obfuscatedScope, obfuscatedRoute) { 8473 * // ... 8474 * } 8475 * // Define function dependencies 8476 * MyController['$inject'] = ['$scope', '$route']; 8477 * 8478 * // Then 8479 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); 8480 * ``` 8481 * 8482 * # The array notation 8483 * 8484 * It is often desirable to inline Injected functions and that's when setting the `$inject` property 8485 * is very inconvenient. In these situations using the array notation to specify the dependencies in 8486 * a way that survives minification is a better choice: 8487 * 8488 * ```js 8489 * // We wish to write this (not minification / obfuscation safe) 8490 * injector.invoke(function($compile, $rootScope) { 8491 * // ... 8492 * }); 8493 * 8494 * // We are forced to write break inlining 8495 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { 8496 * // ... 8497 * }; 8498 * tmpFn.$inject = ['$compile', '$rootScope']; 8499 * injector.invoke(tmpFn); 8500 * 8501 * // To better support inline function the inline annotation is supported 8502 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { 8503 * // ... 8504 * }]); 8505 * 8506 * // Therefore 8507 * expect(injector.annotate( 8508 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) 8509 * ).toEqual(['$compile', '$rootScope']); 8510 * ``` 8511 * 8512 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to 8513 * be retrieved as described above. 8514 * 8515 * @returns {Array.<string>} The names of the services which the function requires. 8516 */ 8517 8518 8519 8520 8521/** 8522 * @ngdoc service 8523 * @name $provide 8524 * 8525 * @description 8526 * 8527 * The {@link auto.$provide $provide} service has a number of methods for registering components 8528 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on 8529 * {@link angular.Module}. 8530 * 8531 * An Angular **service** is a singleton object created by a **service factory**. These **service 8532 * factories** are functions which, in turn, are created by a **service provider**. 8533 * The **service providers** are constructor functions. When instantiated they must contain a 8534 * property called `$get`, which holds the **service factory** function. 8535 * 8536 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the 8537 * correct **service provider**, instantiating it and then calling its `$get` **service factory** 8538 * function to get the instance of the **service**. 8539 * 8540 * Often services have no configuration options and there is no need to add methods to the service 8541 * provider. The provider will be no more than a constructor function with a `$get` property. For 8542 * these cases the {@link auto.$provide $provide} service has additional helper methods to register 8543 * services without specifying a provider. 8544 * 8545 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the 8546 * {@link auto.$injector $injector} 8547 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by 8548 * providers and services. 8549 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by 8550 * services, not providers. 8551 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, 8552 * that will be wrapped in a **service provider** object, whose `$get` property will contain the 8553 * given factory function. 8554 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` 8555 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate 8556 * a new object using the given constructor function. 8557 * 8558 * See the individual methods for more information and examples. 8559 */ 8560 8561/** 8562 * @ngdoc method 8563 * @name $provide#provider 8564 * @description 8565 * 8566 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions 8567 * are constructor functions, whose instances are responsible for "providing" a factory for a 8568 * service. 8569 * 8570 * Service provider names start with the name of the service they provide followed by `Provider`. 8571 * For example, the {@link ng.$log $log} service has a provider called 8572 * {@link ng.$logProvider $logProvider}. 8573 * 8574 * Service provider objects can have additional methods which allow configuration of the provider 8575 * and its service. Importantly, you can configure what kind of service is created by the `$get` 8576 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a 8577 * method {@link ng.$logProvider#debugEnabled debugEnabled} 8578 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the 8579 * console or not. 8580 * 8581 * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 8582 'Provider'` key. 8583 * @param {(Object|function())} provider If the provider is: 8584 * 8585 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using 8586 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. 8587 * - `Constructor`: a new instance of the provider will be created using 8588 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. 8589 * 8590 * @returns {Object} registered provider instance 8591 8592 * @example 8593 * 8594 * The following example shows how to create a simple event tracking service and register it using 8595 * {@link auto.$provide#provider $provide.provider()}. 8596 * 8597 * ```js 8598 * // Define the eventTracker provider 8599 * function EventTrackerProvider() { 8600 * var trackingUrl = '/track'; 8601 * 8602 * // A provider method for configuring where the tracked events should been saved 8603 * this.setTrackingUrl = function(url) { 8604 * trackingUrl = url; 8605 * }; 8606 * 8607 * // The service factory function 8608 * this.$get = ['$http', function($http) { 8609 * var trackedEvents = {}; 8610 * return { 8611 * // Call this to track an event 8612 * event: function(event) { 8613 * var count = trackedEvents[event] || 0; 8614 * count += 1; 8615 * trackedEvents[event] = count; 8616 * return count; 8617 * }, 8618 * // Call this to save the tracked events to the trackingUrl 8619 * save: function() { 8620 * $http.post(trackingUrl, trackedEvents); 8621 * } 8622 * }; 8623 * }]; 8624 * } 8625 * 8626 * describe('eventTracker', function() { 8627 * var postSpy; 8628 * 8629 * beforeEach(module(function($provide) { 8630 * // Register the eventTracker provider 8631 * $provide.provider('eventTracker', EventTrackerProvider); 8632 * })); 8633 * 8634 * beforeEach(module(function(eventTrackerProvider) { 8635 * // Configure eventTracker provider 8636 * eventTrackerProvider.setTrackingUrl('/custom-track'); 8637 * })); 8638 * 8639 * it('tracks events', inject(function(eventTracker) { 8640 * expect(eventTracker.event('login')).toEqual(1); 8641 * expect(eventTracker.event('login')).toEqual(2); 8642 * })); 8643 * 8644 * it('saves to the tracking url', inject(function(eventTracker, $http) { 8645 * postSpy = spyOn($http, 'post'); 8646 * eventTracker.event('login'); 8647 * eventTracker.save(); 8648 * expect(postSpy).toHaveBeenCalled(); 8649 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); 8650 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); 8651 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); 8652 * })); 8653 * }); 8654 * ``` 8655 */ 8656 8657/** 8658 * @ngdoc method 8659 * @name $provide#factory 8660 * @description 8661 * 8662 * Register a **service factory**, which will be called to return the service instance. 8663 * This is short for registering a service where its provider consists of only a `$get` property, 8664 * which is the given service factory function. 8665 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to 8666 * configure your service in a provider. 8667 * 8668 * @param {string} name The name of the instance. 8669 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand 8670 * for `$provide.provider(name, {$get: $getFn})`. 8671 * @returns {Object} registered provider instance 8672 * 8673 * @example 8674 * Here is an example of registering a service 8675 * ```js 8676 * $provide.factory('ping', ['$http', function($http) { 8677 * return function ping() { 8678 * return $http.send('/ping'); 8679 * }; 8680 * }]); 8681 * ``` 8682 * You would then inject and use this service like this: 8683 * ```js 8684 * someModule.controller('Ctrl', ['ping', function(ping) { 8685 * ping(); 8686 * }]); 8687 * ``` 8688 */ 8689 8690 8691/** 8692 * @ngdoc method 8693 * @name $provide#service 8694 * @description 8695 * 8696 * Register a **service constructor**, which will be invoked with `new` to create the service 8697 * instance. 8698 * This is short for registering a service where its provider's `$get` property is the service 8699 * constructor function that will be used to instantiate the service instance. 8700 * 8701 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service 8702 * as a type/class. 8703 * 8704 * @param {string} name The name of the instance. 8705 * @param {Function} constructor A class (constructor function) that will be instantiated. 8706 * @returns {Object} registered provider instance 8707 * 8708 * @example 8709 * Here is an example of registering a service using 8710 * {@link auto.$provide#service $provide.service(class)}. 8711 * ```js 8712 * var Ping = function($http) { 8713 * this.$http = $http; 8714 * }; 8715 * 8716 * Ping.$inject = ['$http']; 8717 * 8718 * Ping.prototype.send = function() { 8719 * return this.$http.get('/ping'); 8720 * }; 8721 * $provide.service('ping', Ping); 8722 * ``` 8723 * You would then inject and use this service like this: 8724 * ```js 8725 * someModule.controller('Ctrl', ['ping', function(ping) { 8726 * ping.send(); 8727 * }]); 8728 * ``` 8729 */ 8730 8731 8732/** 8733 * @ngdoc method 8734 * @name $provide#value 8735 * @description 8736 * 8737 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a 8738 * number, an array, an object or a function. This is short for registering a service where its 8739 * provider's `$get` property is a factory function that takes no arguments and returns the **value 8740 * service**. 8741 * 8742 * Value services are similar to constant services, except that they cannot be injected into a 8743 * module configuration function (see {@link angular.Module#config}) but they can be overridden by 8744 * an Angular 8745 * {@link auto.$provide#decorator decorator}. 8746 * 8747 * @param {string} name The name of the instance. 8748 * @param {*} value The value. 8749 * @returns {Object} registered provider instance 8750 * 8751 * @example 8752 * Here are some examples of creating value services. 8753 * ```js 8754 * $provide.value('ADMIN_USER', 'admin'); 8755 *
8756 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); 8757 * 8758 * $provide.value('halfOf', function(value) { 8759 * return value / 2; 8760 * }); 8761 * ``` 8762 */ 8763 8764 8765/** 8766 * @ngdoc method 8767 * @name $provide#constant 8768 * @description 8769 * 8770 * Register a **constant service**, such as a string, a number, an array, an object or a function, 8771 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be 8772 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot 8773 * be overridden by an Angular {@link auto.$provide#decorator decorator}. 8774 * 8775 * @param {string} name The name of the constant. 8776 * @param {*} value The constant value. 8777 * @returns {Object} registered instance 8778 * 8779 * @example 8780 * Here a some examples of creating constants: 8781 * ```js 8782 * $provide.constant('SHARD_HEIGHT', 306); 8783 * 8784 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); 8785 * 8786 * $provide.constant('double', function(value) { 8787 * return value * 2; 8788 * }); 8789 * ``` 8790 */ 8791 8792 8793/** 8794 * @ngdoc method 8795 * @name $provide#decorator 8796 * @description 8797 * 8798 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator 8799 * intercepts the creation of a service, allowing it to override or modify the behaviour of the 8800 * service. The object returned by the decorator may be the original service, or a new service 8801 * object which replaces or wraps and delegates to the original service. 8802 * 8803 * @param {string} name The name of the service to decorate. 8804 * @param {function()} decorator This function will be invoked when the service needs to be 8805 * instantiated and should return the decorated service instance. The function is called using 8806 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. 8807 * Local injection arguments: 8808 * 8809 * * `$delegate` - The original service instance, which can be monkey patched, configured, 8810 * decorated or delegated to. 8811 * 8812 * @example 8813 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting 8814 * calls to {@link ng.$log#error $log.warn()}. 8815 * ```js 8816 * $provide.decorator('$log', ['$delegate', function($delegate) { 8817 * $delegate.warn = $delegate.error; 8818 * return $delegate; 8819 * }]); 8820 * ``` 8821 */ 8822 8823 8824function createInjector(modulesToLoad) { 8825 var INSTANTIATING = {}, 8826 providerSuffix = 'Provider', 8827 path = [], 8828 loadedModules = new HashMap([], true), 8829 providerCache = { 8830 $provide: { 8831 provider: supportObject(provider), 8832 factory: supportObject(factory), 8833 service: supportObject(service), 8834 value: supportObject(value), 8835 constant: supportObject(constant), 8836 decorator: decorator 8837 } 8838 }, 8839 providerInjector = (providerCache.$injector = 8840 createInternalInjector(providerCache, function() { 8841 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); 8842 })), 8843 instanceCache = {}, 8844 instanceInjector = (instanceCache.$injector = 8845 createInternalInjector(instanceCache, function(servicename) { 8846 var provider = providerInjector.get(servicename + providerSuffix); 8847 return instanceInjector.invoke(provider.$get, provider); 8848 })); 8849 8850 8851 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); 8852 8853 return instanceInjector; 8854 8855 //////////////////////////////////// 8856 // $provider 8857 //////////////////////////////////// 8858 8859 function supportObject(delegate) { 8860 return function(key, value) { 8861 if (isObject(key)) { 8862 forEach(key, reverseParams(delegate)); 8863 } else { 8864 return delegate(key, value); 8865 } 8866 }; 8867 } 8868 8869 function provider(name, provider_) { 8870 assertNotHasOwnProperty(name, 'service'); 8871 if (isFunction(provider_) || isArray(provider_)) { 8872 provider_ = providerInjector.instantiate(provider_); 8873 } 8874 if (!provider_.$get) { 8875 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); 8876 } 8877 return providerCache[name + providerSuffix] = provider_; 8878 } 8879 8880 function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } 8881 8882 function service(name, constructor) { 8883 return factory(name, ['$injector', function($injector) { 8884 return $injector.instantiate(constructor); 8885 }]); 8886 } 8887 8888 function value(name, val) { return factory(name, valueFn(val)); } 8889 8890 function constant(name, value) { 8891 assertNotHasOwnProperty(name, 'constant'); 8892 providerCache[name] = value; 8893 instanceCache[name] = value; 8894 } 8895 8896 function decorator(serviceName, decorFn) { 8897 var origProvider = providerInjector.get(serviceName + providerSuffix), 8898 orig$get = origProvider.$get; 8899 8900 origProvider.$get = function() { 8901 var origInstance = instanceInjector.invoke(orig$get, origProvider); 8902 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); 8903 }; 8904 } 8905 8906 //////////////////////////////////// 8907 // Module Loading 8908 //////////////////////////////////// 8909 function loadModules(modulesToLoad){ 8910 var runBlocks = [], moduleFn, invokeQueue, i, ii;
8911 forEach(modulesToLoad, function(module) { 8912 if (loadedModules.get(module)) return; 8913 loadedModules.put(module, true); 8914 8915 try { 8916 if (isString(module)) { 8917 moduleFn = angularModule(module); 8918 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); 8919 8920 for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { 8921 var invokeArgs = invokeQueue[i], 8922 provider = providerInjector.get(invokeArgs[0]); 8923 8924 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); 8925 } 8926 } else if (isFunction(module)) { 8927 runBlocks.push(providerInjector.invoke(module)); 8928 } else if (isArray(module)) { 8929 runBlocks.push(providerInjector.invoke(module)); 8930 } else { 8931 assertArgFn(module, 'module'); 8932 } 8933 } catch (e) { 8934 if (isArray(module)) { 8935 module = module[module.length - 1]; 8936 } 8937 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { 8938 // Safari & FF's stack traces don't contain error.message content 8939 // unlike those of Chrome and IE 8940 // So if stack doesn't contain message, we create a new string that contains both. 8941 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. 8942 /* jshint -W022 */ 8943 e = e.message + '\n' + e.stack; 8944 } 8945 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", 8946 module, e.stack || e.message || e); 8947 } 8948 }); 8949 return runBlocks; 8950 } 8951 8952 //////////////////////////////////// 8953 // internal Injector 8954 //////////////////////////////////// 8955 8956 function createInternalInjector(cache, factory) { 8957 8958 function getService(serviceName) { 8959 if (cache.hasOwnProperty(serviceName)) { 8960 if (cache[serviceName] === INSTANTIATING) { 8961 throw $injectorMinErr('cdep', 'Circular dependency found: {0}', 8962 serviceName + ' <- ' + path.join(' <- ')); 8963 } 8964 return cache[serviceName]; 8965 } else { 8966 try { 8967 path.unshift(serviceName); 8968 cache[serviceName] = INSTANTIATING; 8969 return cache[serviceName] = factory(serviceName); 8970 } catch (err) { 8971 if (cache[serviceName] === INSTANTIATING) { 8972 delete cache[serviceName]; 8973 } 8974 throw err; 8975 } finally { 8976 path.shift(); 8977 } 8978 } 8979 } 8980 8981 function invoke(fn, self, locals){ 8982 var args = [], 8983 $inject = annotate(fn), 8984 length, i, 8985 key; 8986 8987 for(i = 0, length = $inject.length; i < length; i++) { 8988 key = $inject[i]; 8989 if (typeof key !== 'string') { 8990 throw $injectorMinErr('itkn', 8991 'Incorrect injection token! Expected service name as string, got {0}', key); 8992 } 8993 args.push( 8994 locals && locals.hasOwnProperty(key) 8995 ? locals[key] 8996 : getService(key) 8997 ); 8998 } 8999 if (isArray(fn)) { 9000 fn = fn[length]; 9001 } 9002 9003 // http://jsperf.com/angularjs-invoke-apply-vs-switch 9004 // #5388 9005 return fn.apply(self, args); 9006 } 9007 9008 function instantiate(Type, locals) { 9009 var Constructor = function() {}, 9010 instance, returnedValue; 9011 9012 // Check if Type is annotated and use just the given function at n-1 as parameter 9013 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); 9014 Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; 9015 instance = new Constructor(); 9016 returnedValue = invoke(Type, instance, locals); 9017 9018 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; 9019 } 9020 9021 return { 9022 invoke: invoke, 9023 instantiate: instantiate, 9024 get: getService, 9025 annotate: annotate, 9026 has: function(name) { 9027 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); 9028 } 9029 }; 9030 } 9031} 9032 9033/** 9034 * @ngdoc service 9035 * @name $anchorScroll 9036 * @kind function 9037 * @requires $window 9038 * @requires $location 9039 * @requires $rootScope 9040 * 9041 * @description 9042 * When called, it checks current value of `$location.hash()` and scrolls to the related element, 9043 * according to rules specified in 9044 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). 9045 * 9046 * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. 9047 * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. 9048 * 9049 * @example 9050 <example> 9051 <file name="index.html"> 9052 <div id="scrollArea" ng-controller="ScrollCtrl"> 9053 <a ng-click="gotoBottom()">Go to bottom</a> 9054 <a id="bottom"></a> You're at the bottom! 9055 </div> 9056 </file> 9057 <file name="script.js"> 9058 function ScrollCtrl($scope, $location, $anchorScroll) { 9059 $scope.gotoBottom = function (){ 9060 // set the location.hash to the id of
9061 // the element you wish to scroll to. 9062 $location.hash('bottom'); 9063 9064 // call $anchorScroll() 9065 $anchorScroll(); 9066 }; 9067 } 9068 </file> 9069 <file name="style.css"> 9070 #scrollArea { 9071 height: 350px; 9072 overflow: auto; 9073 } 9074 9075 #bottom { 9076 display: block; 9077 margin-top: 2000px; 9078 } 9079 </file> 9080 </example> 9081 */ 9082function $AnchorScrollProvider() { 9083 9084 var autoScrollingEnabled = true; 9085 9086 this.disableAutoScrolling = function() { 9087 autoScrollingEnabled = false; 9088 }; 9089 9090 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { 9091 var document = $window.document; 9092 9093 // helper function to get first anchor from a NodeList 9094 // can't use filter.filter, as it accepts only instances of Array 9095 // and IE can't convert NodeList to an array using [].slice 9096 // TODO(vojta): use filter if we change it to accept lists as well 9097 function getFirstAnchor(list) { 9098 var result = null; 9099 forEach(list, function(element) { 9100 if (!result && lowercase(element.nodeName) === 'a') result = element; 9101 }); 9102 return result; 9103 } 9104 9105 function scroll() { 9106 var hash = $location.hash(), elm; 9107 9108 // empty hash, scroll to the top of the page 9109 if (!hash) $window.scrollTo(0, 0); 9110 9111 // element with given id 9112 else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); 9113 9114 // first anchor with given name :-D 9115 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); 9116 9117 // no element and hash == 'top', scroll to the top of the page 9118 else if (hash === 'top') $window.scrollTo(0, 0); 9119 } 9120 9121 // does not scroll when user clicks on anchor link that is currently on 9122 // (no url change, no $location.hash() change), browser native does scroll 9123 if (autoScrollingEnabled) { 9124 $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, 9125 function autoScrollWatchAction() { 9126 $rootScope.$evalAsync(scroll); 9127 }); 9128 } 9129 9130 return scroll; 9131 }]; 9132} 9133 9134var $animateMinErr = minErr('$animate'); 9135 9136/** 9137 * @ngdoc provider 9138 * @name $animateProvider 9139 * 9140 * @description 9141 * Default implementation of $animate that doesn't perform any animations, instead just 9142 * synchronously performs DOM 9143 * updates and calls done() callbacks. 9144 * 9145 * In order to enable animations the ngAnimate module has to be loaded. 9146 * 9147 * To see the functional implementation check out src/ngAnimate/animate.js 9148 */ 9149var $AnimateProvider = ['$provide', function($provide) { 9150 9151 9152 this.$$selectors = {}; 9153 9154 9155 /** 9156 * @ngdoc method 9157 * @name $animateProvider#register 9158 * 9159 * @description 9160 * Registers a new injectable animation factory function. The factory function produces the 9161 * animation object which contains callback functions for each event that is expected to be 9162 * animated. 9163 * 9164 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` 9165 * must be called once the element animation is complete. If a function is returned then the 9166 * animation service will use this function to cancel the animation whenever a cancel event is 9167 * triggered. 9168 * 9169 * 9170 * ```js 9171 * return { 9172 * eventFn : function(element, done) { 9173 * //code to run the animation 9174 * //once complete, then run done() 9175 * return function cancellationFunction() { 9176 * //code to cancel the animation 9177 * } 9178 * } 9179 * } 9180 * ``` 9181 * 9182 * @param {string} name The name of the animation. 9183 * @param {Function} factory The factory function that will be executed to return the animation 9184 * object. 9185 */ 9186 this.register = function(name, factory) { 9187 var key = name + '-animation'; 9188 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', 9189 "Expecting class selector starting with '.' got '{0}'.", name); 9190 this.$$selectors[name.substr(1)] = key; 9191 $provide.factory(key, factory); 9192 }; 9193 9194 /** 9195 * @ngdoc method 9196 * @name $animateProvider#classNameFilter 9197 * 9198 * @description 9199 * Sets and/or returns the CSS class regular expression that is checked when performing 9200 * an animation. Upon bootstrap the classNameFilter value is not set at all and will 9201 * therefore enable $animate to attempt to perform an animation on any element. 9202 * When setting the classNameFilter value, animations will only be performed on elements 9203 * that successfully match the filter expression. This in turn can boost performance 9204 * for low-powered devices as well as applications containing a lot of structural operations. 9205 * @param {RegExp=} expression The className expression which will be checked against all animations 9206 * @return {RegExp} The current CSS className expression value. If null then there is no expression value 9207 */ 9208 this.classNameFilter = function(expression) { 9209 if(arguments.length === 1) { 9210 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; 9211 } 9212 return this.$$classNameFilter; 9213 }; 9214 9215 this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) { 9216 9217 function async(fn) { 9218 fn && $$asyncCallback(fn); 9219 } 9220 9221 /** 9222 * 9223 * @ngdoc service 9224 * @name $animate
9225 * @description The $animate service provides rudimentary DOM manipulation functions to 9226 * insert, remove and move elements within the DOM, as well as adding and removing classes. 9227 * This service is the core service used by the ngAnimate $animator service which provides 9228 * high-level animation hooks for CSS and JavaScript. 9229 * 9230 * $animate is available in the AngularJS core, however, the ngAnimate module must be included 9231 * to enable full out animation support. Otherwise, $animate will only perform simple DOM 9232 * manipulation operations. 9233 * 9234 * To learn more about enabling animation support, click here to visit the {@link ngAnimate 9235 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service 9236 * page}. 9237 */ 9238 return { 9239 9240 /** 9241 * 9242 * @ngdoc method 9243 * @name $animate#enter 9244 * @kind function 9245 * @description Inserts the element into the DOM either after the `after` element or within 9246 * the `parent` element. Once complete, the done() callback will be fired (if provided). 9247 * @param {DOMElement} element the element which will be inserted into the DOM 9248 * @param {DOMElement} parent the parent element which will append the element as 9249 * a child (if the after element is not present) 9250 * @param {DOMElement} after the sibling element which will append the element 9251 * after itself 9252 * @param {Function=} done callback function that will be called after the element has been 9253 * inserted into the DOM 9254 */ 9255 enter : function(element, parent, after, done) { 9256 if (after) { 9257 after.after(element); 9258 } else { 9259 if (!parent || !parent[0]) { 9260 parent = after.parent(); 9261 } 9262 parent.append(element); 9263 } 9264 async(done); 9265 }, 9266 9267 /** 9268 * 9269 * @ngdoc method 9270 * @name $animate#leave 9271 * @kind function 9272 * @description Removes the element from the DOM. Once complete, the done() callback will be 9273 * fired (if provided). 9274 * @param {DOMElement} element the element which will be removed from the DOM 9275 * @param {Function=} done callback function that will be called after the element has been 9276 * removed from the DOM 9277 */ 9278 leave : function(element, done) { 9279 element.remove(); 9280 async(done); 9281 }, 9282 9283 /** 9284 * 9285 * @ngdoc method 9286 * @name $animate#move 9287 * @kind function 9288 * @description Moves the position of the provided element within the DOM to be placed 9289 * either after the `after` element or inside of the `parent` element. Once complete, the 9290 * done() callback will be fired (if provided). 9291 * 9292 * @param {DOMElement} element the element which will be moved around within the 9293 * DOM 9294 * @param {DOMElement} parent the parent element where the element will be 9295 * inserted into (if the after element is not present) 9296 * @param {DOMElement} after the sibling element where the element will be 9297 * positioned next to 9298 * @param {Function=} done the callback function (if provided) that will be fired after the 9299 * element has been moved to its new position 9300 */ 9301 move : function(element, parent, after, done) { 9302 // Do not remove element before insert. Removing will cause data associated with the 9303 // element to be dropped. Insert will implicitly do the remove. 9304 this.enter(element, parent, after, done); 9305 }, 9306 9307 /** 9308 * 9309 * @ngdoc method 9310 * @name $animate#addClass 9311 * @kind function 9312 * @description Adds the provided className CSS class value to the provided element. Once 9313 * complete, the done() callback will be fired (if provided). 9314 * @param {DOMElement} element the element which will have the className value 9315 * added to it 9316 * @param {string} className the CSS class which will be added to the element 9317 * @param {Function=} done the callback function (if provided) that will be fired after the 9318 * className value has been added to the element 9319 */ 9320 addClass : function(element, className, done) { 9321 className = isString(className) ? 9322 className : 9323 isArray(className) ? className.join(' ') : '';
9324 forEach(element, function (element) { 9325 jqLiteAddClass(element, className); 9326 }); 9327 async(done); 9328 }, 9329 9330 /** 9331 * 9332 * @ngdoc method 9333 * @name $animate#removeClass 9334 * @kind function 9335 * @description Removes the provided className CSS class value from the provided element. 9336 * Once complete, the done() callback will be fired (if provided). 9337 * @param {DOMElement} element the element which will have the className value 9338 * removed from it 9339 * @param {string} className the CSS class which will be removed from the element 9340 * @param {Function=} done the callback function (if provided) that will be fired after the 9341 * className value has been removed from the element 9342 */ 9343 removeClass : function(element, className, done) { 9344 className = isString(className) ? 9345 className : 9346 isArray(className) ? className.join(' ') : ''; 9347 forEach(element, function (element) { 9348 jqLiteRemoveClass(element, className); 9349 }); 9350 async(done); 9351 }, 9352 9353 /** 9354 * 9355 * @ngdoc method 9356 * @name $animate#setClass 9357 * @kind function 9358 * @description Adds and/or removes the given CSS classes to and from the element. 9359 * Once complete, the done() callback will be fired (if provided). 9360 * @param {DOMElement} element the element which will have its CSS classes changed 9361 * removed from it 9362 * @param {string} add the CSS classes which will be added to the element 9363 * @param {string} remove the CSS class which will be removed from the element 9364 * @param {Function=} done the callback function (if provided) that will be fired after the 9365 * CSS classes have been set on the element 9366 */ 9367 setClass : function(element, add, remove, done) { 9368 forEach(element, function (element) { 9369 jqLiteAddClass(element, add); 9370 jqLiteRemoveClass(element, remove); 9371 }); 9372 async(done); 9373 }, 9374 9375 enabled : noop 9376 }; 9377 }]; 9378}]; 9379 9380function $$AsyncCallbackProvider(){ 9381 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { 9382 return $$rAF.supported 9383 ? function(fn) { return $$rAF(fn); } 9384 : function(fn) { 9385 return $timeout(fn, 0, false); 9386 }; 9387 }]; 9388} 9389 9390/** 9391 * ! This is a private undocumented service ! 9392 * 9393 * @name $browser 9394 * @requires $log 9395 * @description 9396 * This object has two goals: 9397 * 9398 * - hide all the global state in the browser caused by the window object 9399 * - abstract away all the browser specific features and inconsistencies 9400 * 9401 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` 9402 * service, which can be used for convenient testing of the application without the interaction with 9403 * the real browser apis. 9404 */ 9405/** 9406 * @param {object} window The global window object. 9407 * @param {object} document jQuery wrapped document. 9408 * @param {function()} XHR XMLHttpRequest constructor. 9409 * @param {object} $log console.log or an object with the same interface. 9410 * @param {object} $sniffer $sniffer service 9411 */ 9412function Browser(window, document, $log, $sniffer) { 9413 var self = this, 9414 rawDocument = document[0], 9415 location = window.location, 9416 history = window.history, 9417 setTimeout = window.setTimeout, 9418 clearTimeout = window.clearTimeout, 9419 pendingDeferIds = {}; 9420 9421 self.isMock = false; 9422 9423 var outstandingRequestCount = 0; 9424 var outstandingRequestCallbacks = []; 9425 9426 // TODO(vojta): remove this temporary api 9427 self.$$completeOutstandingRequest = completeOutstandingRequest; 9428 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; 9429 9430 /** 9431 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` 9432 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. 9433 */ 9434 function completeOutstandingRequest(fn) { 9435 try { 9436 fn.apply(null, sliceArgs(arguments, 1)); 9437 } finally { 9438 outstandingRequestCount--; 9439 if (outstandingRequestCount === 0) { 9440 while(outstandingRequestCallbacks.length) { 9441 try { 9442 outstandingRequestCallbacks.pop()(); 9443 } catch (e) { 9444 $log.error(e); 9445 } 9446 } 9447 } 9448 } 9449 } 9450 9451 /** 9452 * @private 9453 * Note: this method is used only by scenario runner 9454 * TODO(vojta): prefix this method with $$ ? 9455 * @param {function()} callback Function that will be called when no outstanding request 9456 */ 9457 self.notifyWhenNoOutstandingRequests = function(callback) { 9458 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire 9459 // at some deterministic time in respect to the test runner's actions. Leaving things up to the 9460 // regular poller would result in flaky tests.
9461 forEach(pollFns, function(pollFn){ pollFn(); }); 9462 9463 if (outstandingRequestCount === 0) { 9464 callback(); 9465 } else { 9466 outstandingRequestCallbacks.push(callback); 9467 } 9468 }; 9469 9470 ////////////////////////////////////////////////////////////// 9471 // Poll Watcher API 9472 ////////////////////////////////////////////////////////////// 9473 var pollFns = [], 9474 pollTimeout; 9475 9476 /** 9477 * @name $browser#addPollFn 9478 * 9479 * @param {function()} fn Poll function to add 9480 * 9481 * @description 9482 * Adds a function to the list of functions that poller periodically executes, 9483 * and starts polling if not started yet. 9484 * 9485 * @returns {function()} the added function 9486 */ 9487 self.addPollFn = function(fn) { 9488 if (isUndefined(pollTimeout)) startPoller(100, setTimeout); 9489 pollFns.push(fn); 9490 return fn; 9491 }; 9492 9493 /** 9494 * @param {number} interval How often should browser call poll functions (ms) 9495 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. 9496 * 9497 * @description 9498 * Configures the poller to run in the specified intervals, using the specified 9499 * setTimeout fn and kicks it off. 9500 */ 9501 function startPoller(interval, setTimeout) { 9502 (function check() { 9503 forEach(pollFns, function(pollFn){ pollFn(); }); 9504 pollTimeout = setTimeout(check, interval); 9505 })(); 9506 } 9507 9508 ////////////////////////////////////////////////////////////// 9509 // URL API 9510 ////////////////////////////////////////////////////////////// 9511 9512 var lastBrowserUrl = location.href, 9513 baseElement = document.find('base'), 9514 newLocation = null; 9515 9516 /** 9517 * @name $browser#url 9518 * 9519 * @description 9520 * GETTER: 9521 * Without any argument, this method just returns current value of location.href. 9522 * 9523 * SETTER: 9524 * With at least one argument, this method sets url to new value. 9525 * If html5 history api supported, pushState/replaceState is used, otherwise 9526 * location.href/location.replace is used. 9527 * Returns its own instance to allow chaining 9528 * 9529 * NOTE: this api is intended for use only by the $location service. Please use the 9530 * {@link ng.$location $location service} to change url. 9531 * 9532 * @param {string} url New url (when used as setter) 9533 * @param {boolean=} replace Should new url replace current history record ? 9534 */ 9535 self.url = function(url, replace) { 9536 // Android Browser BFCache causes location, history reference to become stale. 9537 if (location !== window.location) location = window.location; 9538 if (history !== window.history) history = window.history; 9539 9540 // setter 9541 if (url) { 9542 if (lastBrowserUrl == url) return; 9543 lastBrowserUrl = url; 9544 if ($sniffer.history) { 9545 if (replace) history.replaceState(null, '', url); 9546 else { 9547 history.pushState(null, '', url); 9548 // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 9549 baseElement.attr('href', baseElement.attr('href')); 9550 } 9551 } else { 9552 newLocation = url; 9553 if (replace) { 9554 location.replace(url); 9555 } else { 9556 location.href = url; 9557 } 9558 } 9559 return self; 9560 // getter 9561 } else { 9562 // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href 9563 // methods not updating location.href synchronously. 9564 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 9565 return newLocation || location.href.replace(/%27/g,"'"); 9566 } 9567 }; 9568 9569 var urlChangeListeners = [], 9570 urlChangeInit = false; 9571 9572 function fireUrlChange() { 9573 newLocation = null; 9574 if (lastBrowserUrl == self.url()) return; 9575 9576 lastBrowserUrl = self.url(); 9577 forEach(urlChangeListeners, function(listener) { 9578 listener(self.url()); 9579 }); 9580 } 9581 9582 /** 9583 * @name $browser#onUrlChange 9584 * 9585 * @description 9586 * Register callback function that will be called, when url changes. 9587 * 9588 * It's only called when the url is changed from outside of angular: 9589 * - user types different url into address bar 9590 * - user clicks on history (forward/back) button 9591 * - user clicks on a link 9592 * 9593 * It's not called when url is changed by $browser.url() method 9594 * 9595 * The listener gets called with new url as parameter. 9596 * 9597 * NOTE: this api is intended for use only by the $location service. Please use the 9598 * {@link ng.$location $location service} to monitor url changes in angular apps. 9599 * 9600 * @param {function(string)} listener Listener function to be called when url changes. 9601 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. 9602 */ 9603 self.onUrlChange = function(callback) { 9604 // TODO(vojta): refactor to use node's syntax for events 9605 if (!urlChangeInit) { 9606 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) 9607 // don't fire popstate when user change the address bar and don't fire hashchange when url 9608 // changed by push/replaceState 9609 9610 // html5 history api - popstate event 9611 if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange); 9612 // hashchange event 9613 if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange); 9614 // polling 9615 else self.addPollFn(fireUrlChange); 9616 9617 urlChangeInit = true; 9618 } 9619 9620 urlChangeListeners.push(callback); 9621 return callback; 9622 }; 9623 9624 /** 9625 * Checks whether the url has changed outside of Angular. 9626 * Needs to be exported to be able to check for changes that have been done in sync, 9627 * as hashchange/popstate events fire in async. 9628 */ 9629 self.$$checkUrlChange = fireUrlChange; 9630 9631 ////////////////////////////////////////////////////////////// 9632 // Misc API 9633 ////////////////////////////////////////////////////////////// 9634 9635 /** 9636 * @name $browser#baseHref 9637 * 9638 * @description 9639 * Returns current <base href> 9640 * (always relative - without domain) 9641 * 9642 * @returns {string} The current base href 9643 */ 9644 self.baseHref = function() { 9645 var href = baseElement.attr('href'); 9646 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; 9647 }; 9648 9649 ////////////////////////////////////////////////////////////// 9650 // Cookies API 9651 ////////////////////////////////////////////////////////////// 9652 var lastCookies = {}; 9653 var lastCookieString = ''; 9654 var cookiePath = self.baseHref(); 9655 9656 /** 9657 * @name $browser#cookies 9658 * 9659 * @param {string=} name Cookie name 9660 * @param {string=} value Cookie value 9661 * 9662 * @description 9663 * The cookies method provides a 'private' low level access to browser cookies. 9664 * It is not meant to be used directly, use the $cookie service instead. 9665 * 9666 * The return values vary depending on the arguments that the method was called with as follows: 9667 * 9668 * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify 9669 * it 9670 * - cookies(name, value) -> set name to value, if value is undefined delete the cookie 9671 * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that 9672 * way) 9673 * 9674 * @returns {Object} Hash of all cookies (if called without any parameter) 9675 */ 9676 self.cookies = function(name, value) { 9677 /* global escape: false, unescape: false */ 9678 var cookieLength, cookieArray, cookie, i, index; 9679 9680 if (name) { 9681 if (value === undefined) { 9682 rawDocument.cookie = escape(name) + "=;
9682path=" + cookiePath + 9683 ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; 9684 } else { 9685 if (isString(value)) { 9686 cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + 9687 ';path=' + cookiePath).length + 1; 9688 9689 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: 9690 // - 300 cookies 9691 // - 20 cookies per unique domain 9692 // - 4096 bytes per cookie 9693 if (cookieLength > 4096) { 9694 $log.warn("Cookie '"+ name + 9695 "' possibly not set or overflowed because it was too large ("+ 9696 cookieLength + " > 4096 bytes)!"); 9697 } 9698 } 9699 } 9700 } else { 9701 if (rawDocument.cookie !== lastCookieString) { 9702 lastCookieString = rawDocument.cookie; 9703 cookieArray = lastCookieString.split("; "); 9704 lastCookies = {}; 9705 9706 for (i = 0; i < cookieArray.length; i++) { 9707 cookie = cookieArray[i]; 9708 index = cookie.indexOf('='); 9709 if (index > 0) { //ignore nameless cookies 9710 name = unescape(cookie.substring(0, index)); 9711 // the first value that is seen for a cookie is the most 9712 // specific one. values for the same cookie name that 9713 // follow are for less specific paths. 9714 if (lastCookies[name] === undefined) { 9715 lastCookies[name] = unescape(cookie.substring(index + 1)); 9716 } 9717 } 9718 } 9719 } 9720 return lastCookies; 9721 } 9722 }; 9723 9724 9725 /** 9726 * @name $browser#defer 9727 * @param {function()} fn A function, who's execution should be deferred. 9728 * @param {number=} [delay=0] of milliseconds to defer the function execution. 9729 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. 9730 * 9731 * @description 9732 * Executes a fn asynchronously via `setTimeout(fn, delay)`. 9733 * 9734 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using 9735 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed 9736 * via `$browser.defer.flush()`. 9737 * 9738 */ 9739 self.defer = function(fn, delay) { 9740 var timeoutId; 9741 outstandingRequestCount++; 9742 timeoutId = setTimeout(function() { 9743 delete pendingDeferIds[timeoutId]; 9744 completeOutstandingRequest(fn); 9745 }, delay || 0); 9746 pendingDeferIds[timeoutId] = true; 9747 return timeoutId; 9748 }; 9749 9750 9751 /** 9752 * @name $browser#defer.cancel 9753 * 9754 * @description 9755 * Cancels a deferred task identified with `deferId`. 9756 * 9757 * @param {*} deferId Token returned by the `$browser.defer` function. 9758 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully 9759 * canceled. 9760 */ 9761 self.defer.cancel = function(deferId) { 9762 if (pendingDeferIds[deferId]) { 9763 delete pendingDeferIds[deferId]; 9764 clearTimeout(deferId); 9765 completeOutstandingRequest(noop); 9766 return true; 9767 } 9768 return false; 9769 }; 9770 9771} 9772 9773function $BrowserProvider(){ 9774 this.$get = ['$window', '$log', '$sniffer', '$document', 9775 function( $window, $log, $sniffer, $document){ 9776 return new Browser($window, $document, $log, $sniffer); 9777 }]; 9778} 9779 9780/** 9781 * @ngdoc service 9782 * @name $cacheFactory 9783 * 9784 * @description 9785 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to 9786 * them. 9787 * 9788 * ```js 9789 * 9790 * var cache = $cacheFactory('cacheId'); 9791 * expect($cacheFactory.get('cacheId')).toBe(cache); 9792 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); 9793 * 9794 * cache.put("key", "value"); 9795 * cache.put("another key", "another value"); 9796 * 9797 * // We've specified no options on creation 9798 * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); 9799 * 9800 * ``` 9801 * 9802 * 9803 * @param {string} cacheId Name or id of the newly created cache. 9804 * @param {object=} options Options object that specifies the cache behavior. Properties: 9805 * 9806 * - `{number=}` `capacity` â turns the cache into LRU cache. 9807 * 9808 * @returns {object} Newly created cache object with the following set of methods: 9809 * 9810 * - `{object}` `info()` â Returns id, size, and options of cache. 9811 * - `{{*}}` `put({string} key, {*} value)` â Puts a new key-value pair into the cache and returns 9812 * it. 9813 * - `{{*}}` `get({string} key)` â Returns cached value for `key` or undefined for cache miss. 9814 * - `{void}` `remove({string} key)` â Removes a key-value pair from the cache. 9815 * - `{void}` `removeAll()` â Removes all cached values. 9816 * - `{void}` `destroy()` â Removes references to this cache from $cacheFactory. 9817 * 9818 * @example 9819 <example module="cacheExampleApp"> 9820 <file name="index.html"> 9821 <div ng-controller="CacheController"> 9822 <input ng-model="newCacheKey" placeholder="Key"> 9823 <input ng-model="newCacheValue" placeholder="Value"> 9824 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button> 9825 9826 <p ng-if="keys.length">Cached Values</p> 9827 <div ng-repeat="key in keys">
9828 <span ng-bind="key"></span> 9829 <span>: </span> 9830 <b ng-bind="cache.get(key)"></b> 9831 </div> 9832 9833 <p>Cache Info</p> 9834 <div ng-repeat="(key, value) in cache.info()"> 9835 <span ng-bind="key"></span> 9836 <span>: </span> 9837 <b ng-bind="value"></b> 9838 </div> 9839 </div> 9840 </file> 9841 <file name="script.js"> 9842 angular.module('cacheExampleApp', []). 9843 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { 9844 $scope.keys = []; 9845 $scope.cache = $cacheFactory('cacheId'); 9846 $scope.put = function(key, value) { 9847 if ($scope.cache.get(key) === undefined) { 9848 $scope.keys.push(key); 9849 } 9850 $scope.cache.put(key, value === undefined ? null : value); 9851 }; 9852 }]); 9853 </file> 9854 <file name="style.css"> 9855 p { 9856 margin: 10px 0 3px; 9857 } 9858 </file> 9859 </example> 9860 */ 9861function $CacheFactoryProvider() { 9862 9863 this.$get = function() { 9864 var caches = {}; 9865 9866 function cacheFactory(cacheId, options) { 9867 if (cacheId in caches) { 9868 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); 9869 } 9870 9871 var size = 0, 9872 stats = extend({}, options, {id: cacheId}), 9873 data = {}, 9874 capacity = (options && options.capacity) || Number.MAX_VALUE, 9875 lruHash = {}, 9876 freshEnd = null, 9877 staleEnd = null; 9878 9879 /** 9880 * @ngdoc type 9881 * @name $cacheFactory.Cache 9882 * 9883 * @description 9884 * A cache object used to store and retrieve data, primarily used by 9885 * {@link $http $http} and the {@link ng.directive:script script} directive to cache 9886 * templates and other data. 9887 * 9888 * ```js 9889 * angular.module('superCache') 9890 * .factory('superCache', ['$cacheFactory', function($cacheFactory) { 9891 * return $cacheFactory('super-cache'); 9892 * }]); 9893 * ``` 9894 * 9895 * Example test: 9896 * 9897 * ```js 9898 * it('should behave like a cache', inject(function(superCache) { 9899 * superCache.put('key', 'value'); 9900 * superCache.put('another key', 'another value'); 9901 * 9902 * expect(superCache.info()).toEqual({ 9903 * id: 'super-cache', 9904 * size: 2 9905 * }); 9906 * 9907 * superCache.remove('another key'); 9908 * expect(superCache.get('another key')).toBeUndefined(); 9909 * 9910 * superCache.removeAll(); 9911 * expect(superCache.info()).toEqual({ 9912 * id: 'super-cache', 9913 * size: 0 9914 * }); 9915 * })); 9916 * ``` 9917 */ 9918 return caches[cacheId] = { 9919 9920 /** 9921 * @ngdoc method 9922 * @name $cacheFactory.Cache#put 9923 * @kind function 9924 * 9925 * @description 9926 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be 9927 * retrieved later, and incrementing the size of the cache if the key was not already 9928 * present in the cache. If behaving like an LRU cache, it will also remove stale 9929 * entries from the set. 9930 * 9931 * It will not insert undefined values into the cache. 9932 * 9933 * @param {string} key the key under which the cached data is stored. 9934 * @param {*} value the value to store alongside the key. If it is undefined, the key 9935 * will not be stored. 9936 * @returns {*} the value stored. 9937 */ 9938 put: function(key, value) { 9939 if (capacity < Number.MAX_VALUE) { 9940 var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); 9941 9942 refresh(lruEntry); 9943 } 9944 9945 if (isUndefined(value)) return; 9946 if (!(key in data)) size++; 9947 data[key] = value; 9948 9949 if (size > capacity) { 9950 this.remove(staleEnd.key); 9951 } 9952 9953 return value; 9954 }, 9955 9956 /** 9957 * @ngdoc method 9958 * @name $cacheFactory.Cache#get 9959 * @kind function 9960 *
9961 * @description 9962 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. 9963 * 9964 * @param {string} key the key of the data to be retrieved 9965 * @returns {*} the value stored. 9966 */ 9967 get: function(key) { 9968 if (capacity < Number.MAX_VALUE) { 9969 var lruEntry = lruHash[key]; 9970 9971 if (!lruEntry) return; 9972 9973 refresh(lruEntry); 9974 } 9975 9976 return data[key]; 9977 }, 9978 9979 9980 /** 9981 * @ngdoc method 9982 * @name $cacheFactory.Cache#remove 9983 * @kind function 9984 * 9985 * @description 9986 * Removes an entry from the {@link $cacheFactory.Cache Cache} object. 9987 * 9988 * @param {string} key the key of the entry to be removed 9989 */ 9990 remove: function(key) { 9991 if (capacity < Number.MAX_VALUE) { 9992 var lruEntry = lruHash[key]; 9993 9994 if (!lruEntry) return; 9995 9996 if (lruEntry == freshEnd) freshEnd = lruEntry.p; 9997 if (lruEntry == staleEnd) staleEnd = lruEntry.n; 9998 link(lruEntry.n,lruEntry.p); 9999 10000 delete lruHash[key]; 10001 } 10002 10003 delete data[key]; 10004 size--; 10005 }, 10006 10007 10008 /** 10009 * @ngdoc method 10010 * @name $cacheFactory.Cache#removeAll 10011 * @kind function 10012 * 10013 * @description 10014 * Clears the cache object of any entries. 10015 */ 10016 removeAll: function() { 10017 data = {}; 10018 size = 0; 10019 lruHash = {}; 10020 freshEnd = staleEnd = null; 10021 }, 10022 10023 10024 /** 10025 * @ngdoc method 10026 * @name $cacheFactory.Cache#destroy 10027 * @kind function 10028 * 10029 * @description 10030 * Destroys the {@link $cacheFactory.Cache Cache} object entirely, 10031 * removing it from the {@link $cacheFactory $cacheFactory} set. 10032 */ 10033 destroy: function() { 10034 data = null; 10035 stats = null; 10036 lruHash = null; 10037 delete caches[cacheId]; 10038 }, 10039 10040 10041 /** 10042 * @ngdoc method 10043 * @name $cacheFactory.Cache#info 10044 * @kind function 10045 * 10046 * @description 10047 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. 10048 * 10049 * @returns {object} an object with the following properties: 10050 * <ul> 10051 * <li>**id**: the id of the cache instance</li> 10052 * <li>**size**: the number of entries kept in the cache instance</li> 10053 * <li>**...**: any additional properties from the options object when creating the 10054 * cache.</li> 10055 * </ul> 10056 */ 10057 info: function() { 10058 return extend({}, stats, {size: size}); 10059 } 10060 }; 10061 10062 10063 /** 10064 * makes the `entry` the freshEnd of the LRU linked list 10065 */ 10066 function refresh(entry) { 10067 if (entry != freshEnd) { 10068 if (!staleEnd) { 10069 staleEnd = entry; 10070 } else if (staleEnd == entry) { 10071 staleEnd = entry.n;
vendor: 4,905 bytes, lines 10072-10235
10072 } 10073 10074 link(entry.n, entry.p); 10075 link(entry, freshEnd); 10076 freshEnd = entry; 10077 freshEnd.n = null; 10078 } 10079 } 10080 10081 10082 /** 10083 * bidirectionally links two entries of the LRU linked list 10084 */ 10085 function link(nextEntry, prevEntry) { 10086 if (nextEntry != prevEntry) { 10087 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify 10088 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify 10089 } 10090 } 10091 } 10092 10093 10094 /** 10095 * @ngdoc method 10096 * @name $cacheFactory#info 10097 * 10098 * @description 10099 * Get information about all the caches that have been created 10100 * 10101 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` 10102 */ 10103 cacheFactory.info = function() { 10104 var info = {}; 10105 forEach(caches, function(cache, cacheId) { 10106 info[cacheId] = cache.info(); 10107 }); 10108 return info; 10109 }; 10110 10111 10112 /** 10113 * @ngdoc method 10114 * @name $cacheFactory#get 10115 * 10116 * @description 10117 * Get access to a cache object by the `cacheId` used when it was created. 10118 * 10119 * @param {string} cacheId Name or id of a cache to access. 10120 * @returns {object} Cache object identified by the cacheId or undefined if no such cache. 10121 */ 10122 cacheFactory.get = function(cacheId) { 10123 return caches[cacheId]; 10124 }; 10125 10126 10127 return cacheFactory; 10128 }; 10129} 10130 10131/** 10132 * @ngdoc service 10133 * @name $templateCache 10134 * 10135 * @description 10136 * The first time a template is used, it is loaded in the template cache for quick retrieval. You 10137 * can load templates directly into the cache in a `script` tag, or by consuming the 10138 * `$templateCache` service directly. 10139 * 10140 * Adding via the `script` tag: 10141 * 10142 * ```html 10143 * <script type="text/ng-template" id="templateId.html"> 10144 * <p>This is the content of the template</p> 10145 * </script> 10146 * ``` 10147 * 10148 * **Note:** the `script` tag containing the template does not need to be included in the `head` of 10149 * the document, but it must be below the `ng-app` definition. 10150 * 10151 * Adding via the $templateCache service: 10152 * 10153 * ```js 10154 * var myApp = angular.module('myApp', []); 10155 * myApp.run(function($templateCache) { 10156 * $templateCache.put('templateId.html', 'This is the content of the template'); 10157 * }); 10158 * ``` 10159 * 10160 * To retrieve the template later, simply use it in your HTML: 10161 * ```html 10162 * <div ng-include=" 'templateId.html' "></div> 10163 * ``` 10164 * 10165 * or get it via Javascript: 10166 * ```js 10167 * $templateCache.get('templateId.html') 10168 * ``` 10169 * 10170 * See {@link ng.$cacheFactory $cacheFactory}. 10171 * 10172 */ 10173function $TemplateCacheProvider() { 10174 this.$get = ['$cacheFactory', function($cacheFactory) { 10175 return $cacheFactory('templates'); 10176 }]; 10177} 10178 10179/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! 10180 * 10181 * DOM-related variables: 10182 * 10183 * - "node" - DOM Node 10184 * - "element" - DOM Element or Node 10185 * - "$node" or "$element" - jqLite-wrapped node or element 10186 * 10187 * 10188 * Compiler related stuff: 10189 * 10190 * - "linkFn" - linking fn of a single directive 10191 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node 10192 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node 10193 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) 10194 */ 10195 10196 10197/** 10198 * @ngdoc service 10199 * @name $compile 10200 * @kind function 10201 * 10202 * @description 10203 * Compiles an HTML string or DOM into a template and produces a template function, which 10204 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. 10205 * 10206 * The compilation is a process of walking the DOM tree and matching DOM elements to 10207 * {@link ng.$compileProvider#directive directives}. 10208 * 10209 * <div class="alert alert-warning"> 10210 * **Note:** This document is an in-depth reference of all directive options. 10211 * For a gentle introduction to directives with examples of common use cases, 10212 * see the {@link guide/directive directive guide}. 10213 * </div> 10214 * 10215 * ## Comprehensive Directive API 10216 * 10217 * There are many different options for a directive. 10218 * 10219 * The difference resides in the return value of the factory function. 10220 * You can either return a "Directive Definition Object" (see below) that defines the directive properties, 10221 * or just the `postLink` function (all other properties will have the default values). 10222 * 10223 * <div class="alert alert-success"> 10224 * **Best Practice:** It's recommended to use the "directive definition object" form. 10225 * </div> 10226 * 10227 * Here's an example directive declared with a Directive Definition Object: 10228 * 10229 * ```js 10230 * var myModule = angular.module(...); 10231 * 10232 * myModule.directive('directiveName', function factory(injectables) { 10233 * var directiveDefinitionObject = { 10234 * priority: 0, 10235 * template: '<div></div>
10235', // or // function(tElement, tAttrs) { ... }, 10236 * // or 10237 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, 10238 * transclude: false, 10239 * restrict: 'A', 10240 * scope: false, 10241 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, 10242 * controllerAs: 'stringAlias', 10243 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], 10244 * compile: function compile(tElement, tAttrs, transclude) { 10245 * return { 10246 * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, 10247 * post: function postLink(scope, iElement, iAttrs, controller) { ... } 10248 * } 10249 * // or 10250 * // return function postLink( ... ) { ... } 10251 * }, 10252 * // or 10253 * // link: { 10254 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, 10255 * // post: function postLink(scope, iElement, iAttrs, controller) { ... } 10256 * // } 10257 * // or 10258 * // link: function postLink( ... ) { ... } 10259 * }; 10260 * return directiveDefinitionObject; 10261 * }); 10262 * ``` 10263 * 10264 * <div class="alert alert-warning"> 10265 * **Note:** Any unspecified options will use the default value. You can see the default values below. 10266 * </div> 10267 * 10268 * Therefore the above can be simplified as: 10269 * 10270 * ```js 10271 * var myModule = angular.module(...); 10272 * 10273 * myModule.directive('directiveName', function factory(injectables) { 10274 * var directiveDefinitionObject = { 10275 * link: function postLink(scope, iElement, iAttrs) { ... } 10276 * }; 10277 * return directiveDefinitionObject; 10278 * // or 10279 * // return function postLink(scope, iElement, iAttrs) { ... } 10280 * }); 10281 * ``` 10282 * 10283 * 10284 * 10285 * ### Directive Definition Object 10286 * 10287 * The directive definition object provides instructions to the {@link ng.$compile 10288 * compiler}. The attributes are: 10289 * 10290 * #### `priority` 10291 * When there are multiple directives defined on a single DOM element, sometimes it 10292 * is necessary to specify the order in which the directives are applied. The `priority` is used 10293 * to sort the directives before their `compile` functions get called. Priority is defined as a 10294 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions 10295 * are also run in priority order, but post-link functions are run in reverse order. The order 10296 * of directives with the same priority is undefined. The default priority is `0`. 10297 * 10298 * #### `terminal` 10299 * If set to true then the current `priority` will be the last set of directives 10300 * which will execute (any directives at the current priority will still execute 10301 * as the order of execution on same `priority` is undefined). 10302 * 10303 * #### `scope` 10304 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the 10305 * same element request a new scope, only one new scope is created. The new scope rule does not 10306 * apply for the root of the template since the root of the template always gets a new scope. 10307 * 10308 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from 10309 * normal scope in that it does not prototypically inherit from the parent scope. This is useful 10310 * when creating reusable components, which should not accidentally read or modify data in the 10311 * parent scope. 10312 * 10313 * The 'isolate' scope takes an object hash which defines a set of local scope properties 10314 * derived from the parent scope. These local properties are useful for aliasing values for 10315 * templates. Locals definition is a hash of local scope property to its source: 10316 * 10317 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is 10318 * always a string since DOM attributes are strings. If no `attr` name is specified then the 10319 * attribute name is assumed to be the same as the local name. 10320 * Given `<widget my-attr="hello {{name}}">` and widget definition 10321 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect 10322 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the 10323 * `localName` property on the widget scope. The `name` is read from the parent scope (not 10324 * component scope). 10325 * 10326 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the 10327 * parent scope property of name defined via the value of the `attr` attribute. If no `attr` 10328 * name is specified then the attribute name is assumed to be the same as the local name. 10329 * Given `<widget my-attr="parentModel">` and widget definition of 10330 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the 10331 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected 10332 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent 10333 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You 10334 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. 10335 * 10336 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. 10337 * If no `attr` name is specified then the attribute name is assumed to be the same as the 10338 * local name. Given `<widget my-attr="count = count + value">` and widget definition of 10339 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to 10340 * a function wrapper for the `count = count + value` expression. Often it's desirable to 10341 * pass data from the isolated scope via an expression to the parent scope, this can be 10342 * done by passing a map of local variable names and values into the expression wrapper fn. 10343 * For example, if the expression is `increment(amount)` then we can specify the amount value 10344 * by calling the `localFn` as `localFn({amount: 22})`. 10345 * 10346 * 10347 * 10348 * #### `controller` 10349 * Controller constructor function. The controller is instantiated before the 10350 * pre-linking phase and it is shared with other directives (see 10351 * `require` attribute). This allows the directives to communicate with each other and augment 10352 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: 10353 * 10354 * * `$scope` - Current scope associated with the element 10355 * * `$element` - Current element 10356 * * `$attrs` - Current attributes object for the element 10357 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope. 10358 * The scope can be overridden by an optional first argument. 10359 * `function([scope], cloneLinkingFn)`. 10360 * 10361 * 10362 * #### `require` 10363 * Require another directive and inject its controller as the fourth argument to the linking function. The
10364 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the 10365 * injected argument will be an array in corresponding order. If no such directive can be 10366 * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with: 10367 * 10368 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. 10369 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. 10370 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. 10371 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass 10372 * `null` to the `link` fn if not found. 10373 * 10374 * 10375 * #### `controllerAs` 10376 * Controller alias at the directive scope. An alias for the controller so it 10377 * can be referenced at the directive template. The directive needs to define a scope for this 10378 * configuration to be used. Useful in the case when directive is used as component. 10379 * 10380 * 10381 * #### `restrict` 10382 * String of subset of `EACM` which restricts the directive to a specific directive 10383 * declaration style. If omitted, the default (attributes only) is used. 10384 * 10385 * * `E` - Element name: `<my-directive></my-directive>` 10386 * * `A` - Attribute (default): `<div my-directive="exp"></div>` 10387 * * `C` - Class: `<div class="my-directive: exp;"></div>` 10388 * * `M` - Comment: `<!-- directive: my-directive exp -->` 10389 * 10390 * 10391 * #### `template` 10392 * HTML markup that may: 10393 * * Replace the contents of the directive's element (default). 10394 * * Replace the directive's element itself (if `replace` is true - DEPRECATED). 10395 * * Wrap the contents of the directive's element (if `transclude` is true). 10396 * 10397 * Value may be: 10398 * 10399 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`. 10400 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` 10401 * function api below) and returns a string value. 10402 * 10403 * 10404 * #### `templateUrl` 10405 * Same as `template` but the template is loaded from the specified URL. Because 10406 * the template loading is asynchronous the compilation/linking is suspended until the template 10407 * is loaded. 10408 * 10409 * You can specify `templateUrl` as a string representing the URL or as a function which takes two 10410 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns 10411 * a string value representing the url. In either case, the template URL is passed through {@link 10412 * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. 10413 * 10414 * 10415 * #### `replace` ([*DEPRECATED*!], will be removed in next major release) 10416 * specify what the template should replace. Defaults to `false`. 10417 * 10418 * * `true` - the template will replace the directive's element. 10419 * * `false` - the template will replace the contents of the directive's element. 10420 * 10421 * The replacement process migrates all of the attributes / classes from the old element to the new 10422 * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive 10423 * Directives Guide} for an example. 10424 * 10425 * #### `transclude` 10426 * compile the content of the element and make it available to the directive. 10427 * Typically used with {@link ng.directive:ngTransclude 10428 * ngTransclude}. The advantage of transclusion is that the linking function receives a 10429 * transclusion function which is pre-bound to the correct scope. In a typical setup the widget 10430 * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate` 10431 * scope. This makes it possible for the widget to have private state, and the transclusion to 10432 * be bound to the parent (pre-`isolate`) scope. 10433 * 10434 * * `true` - transclude the content of the directive. 10435 * * `'element'` - transclude the whole element including any directives defined at lower priority. 10436 * 10437 * <div class="alert alert-warning"> 10438 * **Note:** When testing an element transclude directive you must not place the directive at the root of the 10439 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives 10440 * Testing Transclusion Directives}. 10441 * </div> 10442 * 10443 * #### `compile` 10444 * 10445 * ```js 10446 * function compile(tElement, tAttrs, transclude) { ... } 10447 * ``` 10448 * 10449 * The compile function deals with transforming the template DOM. Since most directives do not do 10450 * template transformation, it is not used often. The compile function takes the following arguments: 10451 * 10452 * * `tElement` - template element - The element where the directive has been declared. It is 10453 * safe to do template transformation on the element and child elements only. 10454 * 10455 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared 10456 * between all directive compile functions. 10457 * 10458 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` 10459 * 10460 * <div class="alert alert-warning"> 10461 * **Note:** The template instance and the link instance may be different objects if the template has 10462 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that 10463 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
10464 * should be done in a linking function rather than in a compile function. 10465 * </div> 10466 10467 * <div class="alert alert-warning"> 10468 * **Note:** The compile function cannot handle directives that recursively use themselves in their 10469 * own templates or compile functions. Compiling these directives results in an infinite loop and a 10470 * stack overflow errors. 10471 * 10472 * This can be avoided by manually using $compile in the postLink function to imperatively compile 10473 * a directive's template instead of relying on automatic template compilation via `template` or 10474 * `templateUrl` declaration or manual compilation inside the compile function. 10475 * </div> 10476 * 10477 * <div class="alert alert-error"> 10478 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it 10479 * e.g. does not know about the right outer scope. Please use the transclude function that is passed 10480 * to the link function instead. 10481 * </div> 10482 10483 * A compile function can have a return value which can be either a function or an object. 10484 * 10485 * * returning a (post-link) function - is equivalent to registering the linking function via the 10486 * `link` property of the config object when the compile function is empty. 10487 * 10488 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to 10489 * control when a linking function should be called during the linking phase. See info about 10490 * pre-linking and post-linking functions below. 10491 * 10492 * 10493 * #### `link` 10494 * This property is used only if the `compile` property is not defined. 10495 * 10496 * ```js 10497 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } 10498 * ``` 10499 * 10500 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is 10501 * executed after the template has been cloned. This is where most of the directive logic will be 10502 * put. 10503 * 10504 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the 10505 * directive for registering {@link ng.$rootScope.Scope#$watch watches}. 10506 * 10507 * * `iElement` - instance element - The element where the directive is to be used. It is safe to 10508 * manipulate the children of the element only in `postLink` function since the children have 10509 * already been linked. 10510 * 10511 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared 10512 * between all directive linking functions. 10513 * 10514 * * `controller` - a controller instance - A controller instance if at least one directive on the 10515 * element defines a controller. The controller is shared among all the directives, which allows 10516 * the directives to use the controllers as a communication channel. 10517 * 10518 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. 10519 * The scope can be overridden by an optional first argument. This is the same as the `$transclude` 10520 * parameter of directive controllers. 10521 * `function([scope], cloneLinkingFn)`. 10522 * 10523 * 10524 * #### Pre-linking function 10525 * 10526 * Executed before the child elements are linked. Not safe to do DOM transformation since the 10527 * compiler linking function will fail to locate the correct elements for linking. 10528 * 10529 * #### Post-linking function 10530 * 10531 * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function. 10532 * 10533 * <a name="Attributes"></a> 10534 * ### Attributes 10535 * 10536 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the 10537 * `link()` or `compile()` functions. It has a variety of uses. 10538 * 10539 * accessing *Normalized attribute names:* 10540 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. 10541 * the attributes object allows for normalized access to 10542 * the attributes. 10543 * 10544 * * *Directive inter-communication:* All directives share the same instance of the attributes 10545 * object which allows the directives to use the attributes object as inter directive 10546 * communication. 10547 * 10548 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object 10549 * allowing other directives to read the interpolated value. 10550 * 10551 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes 10552 * that contain interpolation (e.g. `src="{{bar}}
10552"`). Not only is this very efficient but it's also 10553 * the only way to easily get the actual value because during the linking phase the interpolation 10554 * hasn't been evaluated yet and so the value is at this time set to `undefined`. 10555 * 10556 * ```js 10557 * function linkingFn(scope, elm, attrs, ctrl) { 10558 * // get the attribute value 10559 * console.log(attrs.ngModel); 10560 * 10561 * // change the attribute 10562 * attrs.$set('ngModel', 'new value'); 10563 * 10564 * // observe changes to interpolated attribute 10565 * attrs.$observe('ngModel', function(value) { 10566 * console.log('ngModel has changed value to ' + value); 10567 * }); 10568 * } 10569 * ``` 10570 * 10571 * Below is an example using `$compileProvider`. 10572 * 10573 * <div class="alert alert-warning"> 10574 * **Note**: Typically directives are registered with `module.directive`. The example below is 10575 * to illustrate how `$compile` works. 10576 * </div> 10577 * 10578 <example module="compileExample"> 10579 <file name="index.html"> 10580 <script> 10581 angular.module('compileExample', [], function($compileProvider) { 10582 // configure new 'compile' directive by passing a directive 10583 // factory function. The factory function injects the '$compile' 10584 $compileProvider.directive('compile', function($compile) { 10585 // directive factory creates a link function 10586 return function(scope, element, attrs) { 10587 scope.$watch( 10588 function(scope) { 10589 // watch the 'compile' expression for changes 10590 return scope.$eval(attrs.compile); 10591 }, 10592 function(value) { 10593 // when the 'compile' expression changes 10594 // assign it into the current DOM 10595 element.html(value); 10596 10597 // compile the new DOM and link it to the current 10598 // scope. 10599 // NOTE: we only compile .childNodes so that 10600 // we don't get into infinite loop compiling ourselves 10601 $compile(element.contents())(scope); 10602 } 10603 ); 10604 }; 10605 }); 10606 }) 10607 .controller('GreeterController', ['$scope', function($scope) { 10608 $scope.name = 'Angular'; 10609 $scope.html = 'Hello {{name}}'; 10610 }]); 10611 </script> 10612 <div ng-controller="GreeterController"> 10613 <input ng-model="name"> <br> 10614 <textarea ng-model="html"></textarea> <br> 10615 <div compile="html"></div> 10616 </div> 10617 </file> 10618 <file name="protractor.js" type="protractor"> 10619 it('should auto compile', function() { 10620 var textarea = $('textarea'); 10621 var output = $('div[compile]'); 10622 // The initial state reads 'Hello Angular'. 10623 expect(output.getText()).toBe('Hello Angular'); 10624 textarea.clear(); 10625 textarea.sendKeys('{{name}}!'); 10626 expect(output.getText()).toBe('Angular!'); 10627 }); 10628 </file> 10629 </example> 10630 10631 * 10632 * 10633 * @param {string|DOMElement} element Element or HTML string to compile into a template function. 10634 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives. 10635 * @param {number} maxPriority only apply directives lower than given priority (Only effects the 10636 * root element(s), not their children) 10637 * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template 10638 * (a DOM element/tree) to a scope. Where: 10639 * 10640 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. 10641 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the 10642 * `template` and call the `cloneAttachFn` function allowing the caller to attach the 10643 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is 10644 * called as: <br> `cloneAttachFn(clonedElement, scope)` where: 10645 * 10646 * * `clonedElement` - is a clone of the original `element` passed into the compiler. 10647 * * `scope` - is the current scope with which the linking function is working with. 10648 * 10649 * Calling the linking function returns the element of the template. It is either the original 10650 * element passed in, or the clone of the element if the `cloneAttachFn` is provided. 10651 * 10652 * After linking the view is not updated until after a call to $digest which typically is done by 10653 * Angular automatically. 10654 * 10655 * If you need access to the bound view, there are two ways to do it: 10656 * 10657 * - If you are not asking the linking function to clone the template, create the DOM element(s) 10658 * before you send them to the compiler and keep this reference around. 10659 * ```js 10660 * var element = $compile('<p>{{total}}</p>')(scope); 10661 * ``` 10662 * 10663 * - if on the other hand, you need the element to be cloned, the view reference from the original 10664 * example would not point to the clone, but rather to the original template that was cloned. In 10665 * this case, you can access the clone via the cloneAttachFn: 10666 * ```js 10667 * var templateElement = angular.element('<p>{{total}}</p>'), 10668 * scope = ....; 10669 * 10670 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { 10671 * //attach the clone to DOM document at the right place 10672 * }); 10673 * 10674 * //now we have reference to the cloned DOM via `clonedElement` 10675 * ``` 10676 * 10677 * 10678 * For information on how the compiler works, see the 10679 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. 10680 */ 10681 10682var $compileMinErr = minErr('$compile'); 10683 10684/** 10685 * @ngdoc provider 10686 * @name $compileProvider 10687 * @kind function 10688 * 10689 * @description 10690 */
10691$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; 10692function $CompileProvider($provide, $$sanitizeUriProvider) { 10693 var hasDirectives = {}, 10694 Suffix = 'Directive', 10695 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/, 10696 CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/; 10697 10698 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes 10699 // The assumption is that future DOM event attribute names will begin with 10700 // 'on' and be composed of only English letters. 10701 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; 10702 10703 /** 10704 * @ngdoc method 10705 * @name $compileProvider#directive 10706 * @kind function 10707 * 10708 * @description 10709 * Register a new directive with the compiler. 10710 * 10711 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which 10712 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the 10713 * names and the values are the factories. 10714 * @param {Function|Array} directiveFactory An injectable directive factory function. See 10715 * {@link guide/directive} for more info. 10716 * @returns {ng.$compileProvider} Self for chaining. 10717 */ 10718 this.directive = function registerDirective(name, directiveFactory) { 10719 assertNotHasOwnProperty(name, 'directive'); 10720 if (isString(name)) { 10721 assertArg(directiveFactory, 'directiveFactory'); 10722 if (!hasDirectives.hasOwnProperty(name)) { 10723 hasDirectives[name] = []; 10724 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', 10725 function($injector, $exceptionHandler) { 10726 var directives = []; 10727 forEach(hasDirectives[name], function(directiveFactory, index) { 10728 try { 10729 var directive = $injector.invoke(directiveFactory); 10730 if (isFunction(directive)) { 10731 directive = { compile: valueFn(directive) }; 10732 } else if (!directive.compile && directive.link) { 10733 directive.compile = valueFn(directive.link); 10734 } 10735 directive.priority = directive.priority || 0; 10736 directive.index = index; 10737 directive.name = directive.name || name; 10738 directive.require = directive.require || (directive.controller && directive.name); 10739 directive.restrict = directive.restrict || 'A'; 10740 directives.push(directive); 10741 } catch (e) { 10742 $exceptionHandler(e); 10743 } 10744 }); 10745 return directives; 10746 }]); 10747 } 10748 hasDirectives[name].push(directiveFactory); 10749 } else { 10750 forEach(name, reverseParams(registerDirective)); 10751 } 10752 return this; 10753 }; 10754 10755 10756 /** 10757 * @ngdoc method 10758 * @name $compileProvider#aHrefSanitizationWhitelist 10759 * @kind function 10760 * 10761 * @description 10762 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 10763 * urls during a[href] sanitization. 10764 * 10765 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 10766 * 10767 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into 10768 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` 10769 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 10770 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 10771 * 10772 * @param {RegExp=} regexp New regexp to whitelist urls with. 10773 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 10774 * chaining otherwise. 10775 */ 10776 this.aHrefSanitizationWhitelist = function(regexp) { 10777 if (isDefined(regexp)) { 10778 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); 10779 return this; 10780 } else { 10781 return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); 10782 } 10783 }; 10784 10785 10786 /** 10787 * @ngdoc method 10788 * @name $compileProvider#imgSrcSanitizationWhitelist 10789 * @kind function 10790 * 10791 * @description 10792 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 10793 * urls during img[src] sanitization. 10794 * 10795 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 10796 * 10797 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into 10798 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` 10799 * regular expression. If a match is found, the original url is
10799written into the dom. Otherwise, 10800 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 10801 * 10802 * @param {RegExp=} regexp New regexp to whitelist urls with. 10803 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 10804 * chaining otherwise. 10805 */ 10806 this.imgSrcSanitizationWhitelist = function(regexp) { 10807 if (isDefined(regexp)) { 10808 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); 10809 return this; 10810 } else { 10811 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); 10812 } 10813 }; 10814 10815 this.$get = [ 10816 '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', 10817 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', 10818 function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, 10819 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { 10820 10821 var Attributes = function(element, attr) { 10822 this.$$element = element; 10823 this.$attr = attr || {}; 10824 }; 10825 10826 Attributes.prototype = { 10827 $normalize: directiveNormalize, 10828 10829 10830 /** 10831 * @ngdoc method 10832 * @name $compile.directive.Attributes#$addClass 10833 * @kind function 10834 * 10835 * @description 10836 * Adds the CSS class value specified by the classVal parameter to the element. If animations 10837 * are enabled then an animation will be triggered for the class addition. 10838 * 10839 * @param {string} classVal The className value that will be added to the element 10840 */ 10841 $addClass : function(classVal) { 10842 if(classVal && classVal.length > 0) { 10843 $animate.addClass(this.$$element, classVal); 10844 } 10845 }, 10846 10847 /** 10848 * @ngdoc method 10849 * @name $compile.directive.Attributes#$removeClass 10850 * @kind function 10851 * 10852 * @description 10853 * Removes the CSS class value specified by the classVal parameter from the element. If 10854 * animations are enabled then an animation will be triggered for the class removal. 10855 * 10856 * @param {string} classVal The className value that will be removed from the element 10857 */ 10858 $removeClass : function(classVal) { 10859 if(classVal && classVal.length > 0) { 10860 $animate.removeClass(this.$$element, classVal); 10861 } 10862 }, 10863 10864 /** 10865 * @ngdoc method 10866 * @name $compile.directive.Attributes#$updateClass 10867 * @kind function 10868 * 10869 * @description 10870 * Adds and removes the appropriate CSS class values to the element based on the difference 10871 * between the new and old CSS class values (specified as newClasses and oldClasses). 10872 * 10873 * @param {string} newClasses The current CSS className value 10874 * @param {string} oldClasses The former CSS className value 10875 */ 10876 $updateClass : function(newClasses, oldClasses) { 10877 var toAdd = tokenDifference(newClasses, oldClasses); 10878 var toRemove = tokenDifference(oldClasses, newClasses); 10879 10880 if(toAdd.length === 0) { 10881 $animate.removeClass(this.$$element, toRemove); 10882 } else if(toRemove.length === 0) { 10883 $animate.addClass(this.$$element, toAdd); 10884 } else { 10885 $animate.setClass(this.$$element, toAdd, toRemove); 10886 } 10887 }, 10888 10889 /** 10890 * Set a normalized attribute on the element in a way such that all directives 10891 * can share the attribute. This function properly handles boolean attributes. 10892 * @param {string} key Normalized key. (ie ngAttribute) 10893 * @param {string|boolean} value The value to set. If `null` attribute will be deleted. 10894 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. 10895 * Defaults to true. 10896 * @param {string=} attrName Optional none normalized name. Defaults to key. 10897 */ 10898 $set: function(key, value, writeAttr, attrName) { 10899 // TODO: decide whether or not to throw an error if "class" 10900 //is set through this function since it may cause $updateClass to 10901 //become unstable. 10902 10903 var booleanKey = getBooleanAttrName(this.$$element[0], key), 10904 normalizedVal, 10905 nodeName; 10906 10907 if (booleanKey) { 10908 this.$$element.prop(key, value); 10909 attrName = booleanKey; 10910 } 10911 10912 this[key] = value; 10913 10914 // translate normalized key to actual key 10915 if (attrName) { 10916 this.$attr[key] = attrName; 10917 } else { 10918 attrName = this.$attr[key]; 10919 if (!attrName) { 10920 this.$attr[key] = attrName = snake_case(key, '-'); 10921 } 10922 } 10923 10924 nodeName = nodeName_(this.$$element); 10925 10926 // sanitize a[href] and img[src] values 10927 if ((nodeName === 'A' && key === 'href') || 10928 (nodeName === 'IMG' && key === 'src')) { 10929 this[key] = value = $$sanitizeUri(value, key === 'src'); 10930 } 10931 10932 if (writeAttr !== false) { 10933 if (value === null || value === undefined) { 10934 this.$$element.removeAttr(attrName); 10935 } else { 10936 this.$$element.attr(attrName, value); 10937 } 10938 } 10939 10940 // fire observers 10941 var $$observers = this.$$observers;
10942 $$observers && forEach($$observers[key], function(fn) { 10943 try { 10944 fn(value); 10945 } catch (e) { 10946 $exceptionHandler(e); 10947 } 10948 }); 10949 }, 10950 10951 10952 /** 10953 * @ngdoc method 10954 * @name $compile.directive.Attributes#$observe 10955 * @kind function 10956 * 10957 * @description 10958 * Observes an interpolated attribute. 10959 * 10960 * The observer function will be invoked once during the next `$digest` following 10961 * compilation. The observer is then invoked whenever the interpolated value 10962 * changes. 10963 * 10964 * @param {string} key Normalized key. (ie ngAttribute) . 10965 * @param {function(interpolatedValue)} fn Function that will be called whenever 10966 the interpolated value of the attribute changes. 10967 * See the {@link guide/directive#Attributes Directives} guide for more info. 10968 * @returns {function()} the `fn` parameter. 10969 */ 10970 $observe: function(key, fn) { 10971 var attrs = this, 10972 $$observers = (attrs.$$observers || (attrs.$$observers = {})), 10973 listeners = ($$observers[key] || ($$observers[key] = [])); 10974 10975 listeners.push(fn); 10976 $rootScope.$evalAsync(function() { 10977 if (!listeners.$$inter) { 10978 // no one registered attribute interpolation function, so lets call it manually 10979 fn(attrs[key]); 10980 } 10981 }); 10982 return fn; 10983 } 10984 }; 10985 10986 var startSymbol = $interpolate.startSymbol(), 10987 endSymbol = $interpolate.endSymbol(), 10988 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') 10989 ? identity 10990 : function denormalizeTemplate(template) { 10991 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); 10992 }, 10993 NG_ATTR_BINDING = /^ngAttr[A-Z]/; 10994 10995 10996 return compile; 10997 10998 //================================ 10999 11000 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, 11001 previousCompileContext) { 11002 if (!($compileNodes instanceof jqLite)) { 11003 // jquery always rewraps, whereas we need to preserve the original selector so that we can 11004 // modify it. 11005 $compileNodes = jqLite($compileNodes); 11006 } 11007 // We can not compile top level text elements since text nodes can be merged and we will 11008 // not be able to attach scope data to them, so we will wrap them in <span> 11009 forEach($compileNodes, function(node, index){ 11010 if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { 11011 $compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0]; 11012 } 11013 }); 11014 var compositeLinkFn = 11015 compileNodes($compileNodes, transcludeFn, $compileNodes, 11016 maxPriority, ignoreDirective, previousCompileContext); 11017 safeAddClass($compileNodes, 'ng-scope'); 11018 return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn){ 11019 assertArg(scope, 'scope'); 11020 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart 11021 // and sometimes changes the structure of the DOM. 11022 var $linkNode = cloneConnectFn 11023 ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! 11024 : $compileNodes; 11025 11026 forEach(transcludeControllers, function(instance, name) { 11027 $linkNode.data('$' + name + 'Controller', instance); 11028 }); 11029 11030 // Attach scope only to non-text nodes. 11031 for(var i = 0, ii = $linkNode.length; i<ii; i++) { 11032 var node = $linkNode[i], 11033 nodeType = node.nodeType; 11034 if (nodeType === 1 /* element */ || nodeType === 9 /* document */) { 11035 $linkNode.eq(i).data('$scope', scope); 11036 } 11037 } 11038 11039 if (cloneConnectFn) cloneConnectFn($linkNode, scope); 11040 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); 11041 return $linkNode; 11042 }; 11043 } 11044 11045 function safeAddClass($element, className) {
11046 try { 11047 $element.addClass(className); 11048 } catch(e) { 11049 // ignore, since it means that we are trying to set class on 11050 // SVG element, where class name is read-only. 11051 } 11052 } 11053 11054 /** 11055 * Compile function matches each node in nodeList against the directives. Once all directives 11056 * for a particular node are collected their compile functions are executed. The compile 11057 * functions return values - the linking functions - are combined into a composite linking 11058 * function, which is the a linking function for the node. 11059 * 11060 * @param {NodeList} nodeList an array of nodes or NodeList to compile 11061 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the 11062 * scope argument is auto-generated to the new child of the transcluded parent scope. 11063 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then 11064 * the rootElement must be set the jqLite collection of the compile root. This is 11065 * needed so that the jqLite collection items can be replaced with widgets. 11066 * @param {number=} maxPriority Max directive priority. 11067 * @returns {Function} A composite linking function of all of the matched directives or null. 11068 */ 11069 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, 11070 previousCompileContext) { 11071 var linkFns = [], 11072 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound; 11073 11074 for (var i = 0; i < nodeList.length; i++) { 11075 attrs = new Attributes(); 11076 11077 // we must always refer to nodeList[i] since the nodes can be replaced underneath us. 11078 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, 11079 ignoreDirective); 11080 11081 nodeLinkFn = (directives.length) 11082 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, 11083 null, [], [], previousCompileContext) 11084 : null; 11085 11086 if (nodeLinkFn && nodeLinkFn.scope) { 11087 safeAddClass(attrs.$$element, 'ng-scope'); 11088 } 11089 11090 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || 11091 !(childNodes = nodeList[i].childNodes) || 11092 !childNodes.length) 11093 ? null 11094 : compileNodes(childNodes, 11095 nodeLinkFn ? ( 11096 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) 11097 && nodeLinkFn.transclude) : transcludeFn); 11098 11099 linkFns.push(nodeLinkFn, childLinkFn); 11100 linkFnFound = linkFnFound || nodeLinkFn || childLinkFn; 11101 //use the previous context only for the first element in the virtual group 11102 previousCompileContext = null; 11103 } 11104 11105 // return a linking function if we have found anything, null otherwise 11106 return linkFnFound ? compositeLinkFn : null; 11107 11108 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { 11109 var nodeLinkFn, childLinkFn, node, childScope, i, ii, n, childBoundTranscludeFn; 11110 11111 // copy nodeList so that linking doesn't break due to live list updates. 11112 var nodeListLength = nodeList.length, 11113 stableNodeList = new Array(nodeListLength); 11114 for (i = 0; i < nodeListLength; i++) { 11115 stableNodeList[i] = nodeList[i]; 11116 } 11117 11118 for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) { 11119 node = stableNodeList[n]; 11120 nodeLinkFn = linkFns[i++]; 11121 childLinkFn = linkFns[i++]; 11122 11123 if (nodeLinkFn) { 11124 if (nodeLinkFn.scope) { 11125 childScope = scope.$new(); 11126 jqLite.data(node, '$scope', childScope); 11127 } else { 11128 childScope = scope; 11129 } 11130 11131 if ( nodeLinkFn.transcludeOnThisElement ) { 11132 childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
11133 11134 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { 11135 childBoundTranscludeFn = parentBoundTranscludeFn; 11136 11137 } else if (!parentBoundTranscludeFn && transcludeFn) { 11138 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); 11139 11140 } else { 11141 childBoundTranscludeFn = null; 11142 } 11143 11144 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); 11145 11146 } else if (childLinkFn) { 11147 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); 11148 } 11149 } 11150 } 11151 } 11152 11153 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { 11154 11155 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers) { 11156 var scopeCreated = false; 11157 11158 if (!transcludedScope) { 11159 transcludedScope = scope.$new(); 11160 transcludedScope.$$transcluded = true; 11161 scopeCreated = true; 11162 } 11163 11164 var clone = transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn); 11165 if (scopeCreated) { 11166 clone.on('$destroy', function() { transcludedScope.$destroy(); }); 11167 } 11168 return clone; 11169 }; 11170 11171 return boundTranscludeFn; 11172 } 11173 11174 /** 11175 * Looks for directives on the given node and adds them to the directive collection which is 11176 * sorted. 11177 * 11178 * @param node Node to search. 11179 * @param directives An array to which the directives are added to. This array is sorted before 11180 * the function returns. 11181 * @param attrs The shared attrs object which is used to populate the normalized attributes. 11182 * @param {number=} maxPriority Max directive priority. 11183 */ 11184 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { 11185 var nodeType = node.nodeType, 11186 attrsMap = attrs.$attr, 11187 match, 11188 className; 11189 11190 switch(nodeType) { 11191 case 1: /* Element */ 11192 // use the node name: <directive> 11193 addDirective(directives, 11194 directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); 11195 11196 // iterate over the attributes 11197 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, 11198 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { 11199 var attrStartName = false; 11200 var attrEndName = false; 11201 11202 attr = nAttrs[j]; 11203 if (!msie || msie >= 8 || attr.specified) { 11204 name = attr.name; 11205 value = trim(attr.value); 11206 11207 // support ngAttr attribute binding 11208 ngAttrName = directiveNormalize(name); 11209 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { 11210 name = snake_case(ngAttrName.substr(6), '-'); 11211 } 11212 11213 var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); 11214 if (ngAttrName === directiveNName + 'Start') { 11215 attrStartName = name; 11216 attrEndName = name.substr(0, name.length - 5) + 'end'; 11217 name = name.substr(0, name.length - 6); 11218 } 11219 11220 nName = directiveNormalize(name.toLowerCase()); 11221 attrsMap[nName] = name; 11222 if (isNgAttr || !attrs.hasOwnProperty(nName)) { 11223 attrs[nName] = value; 11224 if (getBooleanAttrName(node, nName)) { 11225 attrs[nName] = true; // presence means true 11226 } 11227 } 11228 addAttrInterpolateDirective(node, directives, value, nName); 11229 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, 11230 attrEndName); 11231 } 11232 } 11233 11234 // use class as directive 11235 className = node.className; 11236 if (isString(className) && className !== '') { 11237 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { 11238 nName = directiveNormalize(match[2]); 11239 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { 11240 attrs[nName] = trim(match[3]); 11241 } 11242 className = className.substr(match.index + match[0].length); 11243 } 11244 } 11245 break; 11246 case 3: /* Text Node */ 11247 addTextInterpolateDirective(directives, node.nodeValue); 11248 break; 11249 case 8: /* Comment */ 11250 try { 11251 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); 11252 if (match) { 11253 nName = directiveNormalize(match[1]); 11254 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { 11255 attrs[nName] = trim(match[2]); 11256 } 11257 } 11258 } catch (e) { 11259 // turns out that under some circumstances IE9 throws errors when one attempts to read 11260 // comment's node value. 11261 // Just ignore it and continue. (Can't seem to reproduce in test case.) 11262 } 11263 break; 11264 } 11265 11266 directives.sort(byPriority); 11267 return directives; 11268 } 11269 11270 /**
11271 * Given a node with an directive-start it collects all of the siblings until it finds 11272 * directive-end. 11273 * @param node 11274 * @param attrStart 11275 * @param attrEnd 11276 * @returns {*} 11277 */ 11278 function groupScan(node, attrStart, attrEnd) { 11279 var nodes = []; 11280 var depth = 0; 11281 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { 11282 var startNode = node; 11283 do { 11284 if (!node) { 11285 throw $compileMinErr('uterdir', 11286 "Unterminated attribute, found '{0}' but no matching '{1}' found.", 11287 attrStart, attrEnd); 11288 } 11289 if (node.nodeType == 1 /** Element **/) { 11290 if (node.hasAttribute(attrStart)) depth++; 11291 if (node.hasAttribute(attrEnd)) depth--; 11292 } 11293 nodes.push(node); 11294 node = node.nextSibling; 11295 } while (depth > 0); 11296 } else { 11297 nodes.push(node); 11298 } 11299 11300 return jqLite(nodes); 11301 } 11302 11303 /** 11304 * Wrapper for linking function which converts normal linking function into a grouped 11305 * linking function. 11306 * @param linkFn 11307 * @param attrStart 11308 * @param attrEnd 11309 * @returns {Function} 11310 */ 11311 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { 11312 return function(scope, element, attrs, controllers, transcludeFn) { 11313 element = groupScan(element[0], attrStart, attrEnd); 11314 return linkFn(scope, element, attrs, controllers, transcludeFn); 11315 }; 11316 } 11317 11318 /** 11319 * Once the directives have been collected, their compile functions are executed. This method 11320 * is responsible for inlining directive templates as well as terminating the application 11321 * of the directives if the terminal directive has been reached. 11322 * 11323 * @param {Array} directives Array of collected directives to execute their compile function. 11324 * this needs to be pre-sorted by priority order. 11325 * @param {Node} compileNode The raw DOM node to apply the compile functions to 11326 * @param {Object} templateAttrs The shared attribute function 11327 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the 11328 * scope argument is auto-generated to the new 11329 * child of the transcluded parent scope. 11330 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this 11331 * argument has the root jqLite array so that we can replace nodes 11332 * on it. 11333 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when 11334 * compiling the transclusion. 11335 * @param {Array.<Function>} preLinkFns 11336 * @param {Array.<Function>} postLinkFns 11337 * @param {Object} previousCompileContext Context used for previous compilation of the current 11338 * node 11339 * @returns {Function} linkFn 11340 */ 11341 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, 11342 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, 11343 previousCompileContext) { 11344 previousCompileContext = previousCompileContext || {}; 11345 11346 var terminalPriority = -Number.MAX_VALUE, 11347 newScopeDirective, 11348 controllerDirectives = previousCompileContext.controllerDirectives, 11349 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, 11350 templateDirective = previousCompileContext.templateDirective, 11351 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, 11352 hasTranscludeDirective = false, 11353 hasTemplate = false, 11354 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, 11355 $compileNode = templateAttrs.$$element = jqLite(compileNode), 11356 directive, 11357 directiveName, 11358 $template, 11359 replaceDirective = originalReplaceDirective, 11360 childTranscludeFn = transcludeFn, 11361 linkFn, 11362 directiveValue; 11363 11364 // executes all directives on the current element 11365 for(var i = 0, ii = directives.length; i < ii; i++) { 11366 directive = directives[i]; 11367 var attrStart = directive.$$start; 11368 var attrEnd = directive.$$end; 11369 11370 // collect multiblock sections 11371 if (attrStart) { 11372 $compileNode = groupScan(compileNode, attrStart, attrEnd); 11373 } 11374 $template = undefined; 11375 11376 if (terminalPriority > directive.priority) { 11377 break; // prevent further processing of directives 11378 } 11379 11380 if (directiveValue = directive.scope) { 11381 newScopeDirective = newScopeDirective || directive; 11382 11383 // skip the check for directives with async templates, we'll check the derived sync 11384 // directive when the template arrives 11385 if (!directive.templateUrl) { 11386 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, 11387 $compileNode); 11388 if (isObject(directiveValue)) { 11389 newIsolateScopeDirective = directive; 11390 } 11391 } 11392 } 11393 11394 directiveName = directive.name; 11395 11396 if (!directive.templateUrl && directive.controller) { 11397 directiveValue = directive.controller; 11398 controllerDirectives = controllerDirectives || {}; 11399 assertNoDuplicate("'" + directiveName + "' controller", 11400 controllerDirectives[directiveName], directive, $compileNode); 11401 controllerDirectives[directiveName] = directive; 11402 } 11403 11404 if (directiveValue = directive.transclude) { 11405 hasTranscludeDirective = true; 11406 11407 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. 11408 // This option should only be used by directives that know how to safely handle element transclusion, 11409 // where the transcluded nodes are added or replaced after linking. 11410 if (!directive.$$tlb) { 11411 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); 11412 nonTlbTranscludeDirective = directive; 11413 } 11414 11415 if (directiveValue == 'element') { 11416 hasElementTranscludeDirective = true; 11417 terminalPriority = directive.priority; 11418 $template = $compileNode; 11419 $compileNode = templateAttrs.$$element = 11420 jqLite(document.createComment(' ' + directiveName + ': ' + 11421 templateAttrs[directiveName] + ' ')); 11422 compileNode = $compileNode[0]; 11423 replaceWith(jqCollection, sliceArgs($template), compileNode); 11424 11425 childTranscludeFn = compile($template, transcludeFn, terminalPriority, 11426 replaceDirective && replaceDirective.name, { 11427 // Don't pass in: 11428 // - controllerDirectives - otherwise we'll create duplicates controllers 11429 // - newIsolateScopeDirective or templateDirective - comb
11429ining templates with 11430 // element transclusion doesn't make sense. 11431 // 11432 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion 11433 // on the same element more than once. 11434 nonTlbTranscludeDirective: nonTlbTranscludeDirective 11435 }); 11436 } else { 11437 $template = jqLite(jqLiteClone(compileNode)).contents(); 11438 $compileNode.empty(); // clear contents 11439 childTranscludeFn = compile($template, transcludeFn); 11440 } 11441 } 11442 11443 if (directive.template) { 11444 hasTemplate = true; 11445 assertNoDuplicate('template', templateDirective, directive, $compileNode); 11446 templateDirective = directive; 11447 11448 directiveValue = (isFunction(directive.template)) 11449 ? directive.template($compileNode, templateAttrs) 11450 : directive.template; 11451 11452 directiveValue = denormalizeTemplate(directiveValue); 11453 11454 if (directive.replace) { 11455 replaceDirective = directive; 11456 if (jqLiteIsTextNode(directiveValue)) { 11457 $template = []; 11458 } else { 11459 $template = jqLite(trim(directiveValue)); 11460 } 11461 compileNode = $template[0]; 11462 11463 if ($template.length != 1 || compileNode.nodeType !== 1) { 11464 throw $compileMinErr('tplrt', 11465 "Template for directive '{0}' must have exactly one root element. {1}", 11466 directiveName, ''); 11467 } 11468 11469 replaceWith(jqCollection, $compileNode, compileNode); 11470 11471 var newTemplateAttrs = {$attr: {}}; 11472 11473 // combine directives from the original node and from the template: 11474 // - take the array of directives for this element 11475 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) 11476 // - collect directives from the template and sort them by priority 11477 // - combine directives as: processed + template + unprocessed 11478 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); 11479 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); 11480 11481 if (newIsolateScopeDirective) { 11482 markDirectivesAsIsolate(templateDirectives); 11483 } 11484 directives = directives.concat(templateDirectives).concat(unprocessedDirectives); 11485 mergeTemplateAttributes(templateAttrs, newTemplateAttrs); 11486 11487 ii = directives.length; 11488 } else { 11489 $compileNode.html(directiveValue); 11490 } 11491 } 11492 11493 if (directive.templateUrl) { 11494 hasTemplate = true; 11495 assertNoDuplicate('template', templateDirective, directive, $compileNode); 11496 templateDirective = directive; 11497 11498 if (directive.replace) { 11499 replaceDirective = directive; 11500 } 11501 11502 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, 11503 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { 11504 controllerDirectives: controllerDirectives, 11505 newIsolateScopeDirective: newIsolateScopeDirective, 11506 templateDirective: templateDirective, 11507 nonTlbTranscludeDirective: nonTlbTranscludeDirective 11508 }); 11509 ii = directives.length; 11510 } else if (directive.compile) { 11511 try { 11512 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); 11513 if (isFunction(linkFn)) { 11514 addLinkFns(null, linkFn, attrStart, attrEnd); 11515 } else if (linkFn) { 11516 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); 11517 } 11518 } catch (e) { 11519 $exceptionHandler(e, startingTag($compileNode)); 11520 } 11521 } 11522 11523 if (directive.terminal) { 11524 nodeLinkFn.terminal = true; 11525 terminalPriority = Math.max(terminalPriority, directive.priority); 11526 } 11527 11528 } 11529 11530 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; 11531 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; 11532 nodeLinkFn.templateOnThisElement = hasTemplate; 11533 nodeLinkFn.transclude = childTranscludeFn; 11534 11535 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; 11536
11537 // might be normal or delayed nodeLinkFn depending on if templateUrl is present 11538 return nodeLinkFn; 11539 11540 //////////////////// 11541 11542 function addLinkFns(pre, post, attrStart, attrEnd) { 11543 if (pre) { 11544 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); 11545 pre.require = directive.require; 11546 pre.directiveName = directiveName; 11547 if (newIsolateScopeDirective === directive || directive.$$isolateScope) { 11548 pre = cloneAndAnnotateFn(pre, {isolateScope: true}); 11549 } 11550 preLinkFns.push(pre); 11551 } 11552 if (post) { 11553 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); 11554 post.require = directive.require; 11555 post.directiveName = directiveName; 11556 if (newIsolateScopeDirective === directive || directive.$$isolateScope) { 11557 post = cloneAndAnnotateFn(post, {isolateScope: true}); 11558 } 11559 postLinkFns.push(post); 11560 } 11561 } 11562 11563 11564 function getControllers(directiveName, require, $element, elementControllers) { 11565 var value, retrievalMethod = 'data', optional = false; 11566 if (isString(require)) { 11567 while((value = require.charAt(0)) == '^' || value == '?') { 11568 require = require.substr(1); 11569 if (value == '^') { 11570 retrievalMethod = 'inheritedData'; 11571 } 11572 optional = optional || value == '?'; 11573 } 11574 value = null; 11575 11576 if (elementControllers && retrievalMethod === 'data') { 11577 value = elementControllers[require]; 11578 } 11579 value = value || $element[retrievalMethod]('$' + require + 'Controller'); 11580 11581 if (!value && !optional) { 11582 throw $compileMinErr('ctreq', 11583 "Controller '{0}', required by directive '{1}', can't be found!", 11584 require, directiveName); 11585 } 11586 return value; 11587 } else if (isArray(require)) { 11588 value = []; 11589 forEach(require, function(require) { 11590 value.push(getControllers(directiveName, require, $element, elementControllers)); 11591 }); 11592 } 11593 return value; 11594 } 11595 11596 11597 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { 11598 var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn; 11599 11600 attrs = (compileNode === linkNode) 11601 ? templateAttrs 11602 : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); 11603 $element = attrs.$$element; 11604 11605 if (newIsolateScopeDirective) { 11606 var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; 11607 11608 isolateScope = scope.$new(true); 11609 11610 if (templateDirective && (templateDirective === newIsolateScopeDirective || 11611 templateDirective === newIsolateScopeDirective.$$originalDirective)) { 11612 $element.data('$isolateScope', isolateScope); 11613 } else { 11614 $element.data('$isolateScopeNoTemplate', isolateScope); 11615 } 11616 11617 11618 11619 safeAddClass($element, 'ng-isolate-scope'); 11620 11621 forEach(newIsolateScopeDirective.scope, function(definition, scopeName) { 11622 var match = definition.match(LOCAL_REGEXP) || [], 11623 attrName = match[3] || scopeName, 11624 optional = (match[2] == '?'), 11625 mode = match[1], // @, =, or & 11626 lastValue, 11627 parentGet, parentSet, compare; 11628 11629 isolateScope.$$isolateBindings[scopeName] = mode + attrName; 11630 11631 switch (mode) { 11632 11633 case '@': 11634 attrs.$observe(attrName, function(value) { 11635 isolateScope[scopeName] = value; 11636 }); 11637 attrs.$$observers[attrName].$$scope = scope; 11638 if( attrs[attrName] ) { 11639 // If the attribute has been provided then we trigger an interpolation to ensure 11640 // the value is there for use in the link fn 11641 isolateScope[scopeName] = $interpolate(attrs[attrName])(scope); 11642 } 11643 break; 11644 11645 case '=': 11646 if (optional && !attrs[attrName]) { 11647 return; 11648 } 11649 parentGet = $parse(attrs[attrName]); 11650 if (parentGet.literal) { 11651 compare = equals; 11652 } else { 11653 compare = function(a,b) { return a === b || (a !== a && b !== b); }; 11654 } 11655 parentSet = parentGet.assign || function() { 11656 // reset the change, or we will throw this exception on every $digest
11657 lastValue = isolateScope[scopeName] = parentGet(scope); 11658 throw $compileMinErr('nonassign', 11659 "Expression '{0}' used with directive '{1}' is non-assignable!", 11660 attrs[attrName], newIsolateScopeDirective.name); 11661 }; 11662 lastValue = isolateScope[scopeName] = parentGet(scope); 11663 isolateScope.$watch(function parentValueWatch() { 11664 var parentValue = parentGet(scope); 11665 if (!compare(parentValue, isolateScope[scopeName])) { 11666 // we are out of sync and need to copy 11667 if (!compare(parentValue, lastValue)) { 11668 // parent changed and it has precedence 11669 isolateScope[scopeName] = parentValue; 11670 } else { 11671 // if the parent can be assigned then do so 11672 parentSet(scope, parentValue = isolateScope[scopeName]); 11673 } 11674 } 11675 return lastValue = parentValue; 11676 }, null, parentGet.literal); 11677 break; 11678 11679 case '&': 11680 parentGet = $parse(attrs[attrName]); 11681 isolateScope[scopeName] = function(locals) { 11682 return parentGet(scope, locals); 11683 }; 11684 break; 11685 11686 default: 11687 throw $compileMinErr('iscp', 11688 "Invalid isolate scope definition for directive '{0}'." + 11689 " Definition: {... {1}: '{2}' ...}", 11690 newIsolateScopeDirective.name, scopeName, definition); 11691 } 11692 }); 11693 } 11694 transcludeFn = boundTranscludeFn && controllersBoundTransclude; 11695 if (controllerDirectives) { 11696 forEach(controllerDirectives, function(directive) { 11697 var locals = { 11698 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, 11699 $element: $element, 11700 $attrs: attrs, 11701 $transclude: transcludeFn 11702 }, controllerInstance; 11703 11704 controller = directive.controller; 11705 if (controller == '@') { 11706 controller = attrs[directive.name]; 11707 } 11708 11709 controllerInstance = $controller(controller, locals); 11710 // For directives with element transclusion the element is a comment, 11711 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to 11712 // clean up (http://bugs.jquery.com/ticket/8335). 11713 // Instead, we save the controllers for the element in a local hash and attach to .data 11714 // later, once we have the actual element. 11715 elementControllers[directive.name] = controllerInstance; 11716 if (!hasElementTranscludeDirective) { 11717 $element.data('$' + directive.name + 'Controller', controllerInstance); 11718 } 11719 11720 if (directive.controllerAs) { 11721 locals.$scope[directive.controllerAs] = controllerInstance; 11722 } 11723 }); 11724 } 11725 11726 // PRELINKING 11727 for(i = 0, ii = preLinkFns.length; i < ii; i++) { 11728 try { 11729 linkFn = preLinkFns[i]; 11730 linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, 11731 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); 11732 } catch (e) { 11733 $exceptionHandler(e, startingTag($element)); 11734 } 11735 } 11736 11737 // RECURSION 11738 // We only pass the isolate scope, if the isolate directive has a template, 11739 // otherwise the child elements do not belong to the isolate directive. 11740 var scopeToChild = scope; 11741 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { 11742 scopeToChild = isolateScope; 11743 } 11744 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
11745 11746 // POSTLINKING 11747 for(i = postLinkFns.length - 1; i >= 0; i--) { 11748 try { 11749 linkFn = postLinkFns[i]; 11750 linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, 11751 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); 11752 } catch (e) { 11753 $exceptionHandler(e, startingTag($element)); 11754 } 11755 } 11756 11757 // This is the function that is injected as `$transclude`. 11758 function controllersBoundTransclude(scope, cloneAttachFn) { 11759 var transcludeControllers; 11760 11761 // no scope passed 11762 if (arguments.length < 2) { 11763 cloneAttachFn = scope; 11764 scope = undefined; 11765 } 11766 11767 if (hasElementTranscludeDirective) { 11768 transcludeControllers = elementControllers; 11769 } 11770 11771 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers); 11772 } 11773 } 11774 } 11775 11776 function markDirectivesAsIsolate(directives) { 11777 // mark all directives as needing isolate scope. 11778 for (var j = 0, jj = directives.length; j < jj; j++) { 11779 directives[j] = inherit(directives[j], {$$isolateScope: true}); 11780 } 11781 } 11782 11783 /** 11784 * looks up the directive and decorates it with exception handling and proper parameters. We 11785 * call this the boundDirective. 11786 * 11787 * @param {string} name name of the directive to look up. 11788 * @param {string} location The directive must be found in specific format. 11789 * String containing any of theses characters: 11790 * 11791 * * `E`: element name 11792 * * `A': attribute 11793 * * `C`: class 11794 * * `M`: comment 11795 * @returns {boolean} true if directive was added. 11796 */ 11797 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, 11798 endAttrName) { 11799 if (name === ignoreDirective) return null; 11800 var match = null; 11801 if (hasDirectives.hasOwnProperty(name)) { 11802 for(var directive, directives = $injector.get(name + Suffix), 11803 i = 0, ii = directives.length; i<ii; i++) { 11804 try { 11805 directive = directives[i]; 11806 if ( (maxPriority === undefined || maxPriority > directive.priority) && 11807 directive.restrict.indexOf(location) != -1) { 11808 if (startAttrName) { 11809 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); 11810 } 11811 tDirectives.push(directive); 11812 match = directive; 11813 } 11814 } catch(e) { $exceptionHandler(e); } 11815 } 11816 } 11817 return match; 11818 } 11819 11820 11821 /** 11822 * When the element is replaced with HTML template then the new attributes 11823 * on the template need to be merged with the existing attributes in the DOM. 11824 * The desired effect is to have both of the attributes present. 11825 * 11826 * @param {object} dst destination attributes (original DOM) 11827 * @param {object} src source attributes (from the directive template) 11828 */ 11829 function mergeTemplateAttributes(dst, src) { 11830 var srcAttr = src.$attr, 11831 dstAttr = dst.$attr, 11832 $element = dst.$$element; 11833 11834 // reapply the old attributes to the new element 11835 forEach(dst, function(value, key) { 11836 if (key.charAt(0) != '$') { 11837 if (src[key] && src[key] !== value) { 11838 value += (key === 'style' ? ';' : ' ') + src[key]; 11839 } 11840 dst.$set(key, value, true, srcAttr[key]); 11841 } 11842 }); 11843 11844 // copy the new attributes on the old attrs object 11845 forEach(src, function(value, key) { 11846 if (key == 'class') { 11847 safeAddClass($element, value); 11848 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; 11849 } else if (key == 'style') { 11850 $element.attr('style', $element.attr('style') + ';' + value); 11851 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; 11852 // `dst` will never contain hasOwnProperty as DOM parser won't let it. 11853 // You will get an "InvalidCharacterError: DOM Exception 5" error if you 11854 // have an attribute like "has-own-property" or "data-has-own-property", etc. 11855 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { 11856 dst[key] = value; 11857 dstAttr[key] = srcAttr[key]; 11858 } 11859 }); 11860 } 11861 11862 11863 function compileTemplateUrl(directives, $compileNode, tAttrs, 11864 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { 11865 var linkQueue = [], 11866 afterTemplateNodeLinkFn, 11867 afterTemplateChildLinkFn, 11868 beforeTemplateCompileNode = $compileNode[0], 11869 origAsyncDirective = directives.shift(), 11870 // The fact that we have to copy and patch the directive seems wrong! 11871 derivedSyncDirective = extend({}, origAsyncDirective, { 11872 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective 11873 }), 11874 templateUrl = (isFunction(origAsyncDirective.templateUrl)) 11875 ? origAsyncDirective.templateUrl($compileNode, tAttrs
11875) 11876 : origAsyncDirective.templateUrl; 11877 11878 $compileNode.empty(); 11879 11880 $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}). 11881 success(function(content) { 11882 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; 11883 11884 content = denormalizeTemplate(content); 11885 11886 if (origAsyncDirective.replace) { 11887 if (jqLiteIsTextNode(content)) { 11888 $template = []; 11889 } else { 11890 $template = jqLite(trim(content)); 11891 } 11892 compileNode = $template[0]; 11893 11894 if ($template.length != 1 || compileNode.nodeType !== 1) { 11895 throw $compileMinErr('tplrt', 11896 "Template for directive '{0}' must have exactly one root element. {1}", 11897 origAsyncDirective.name, templateUrl); 11898 } 11899 11900 tempTemplateAttrs = {$attr: {}}; 11901 replaceWith($rootElement, $compileNode, compileNode); 11902 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); 11903 11904 if (isObject(origAsyncDirective.scope)) { 11905 markDirectivesAsIsolate(templateDirectives); 11906 } 11907 directives = templateDirectives.concat(directives); 11908 mergeTemplateAttributes(tAttrs, tempTemplateAttrs); 11909 } else { 11910 compileNode = beforeTemplateCompileNode; 11911 $compileNode.html(content); 11912 } 11913 11914 directives.unshift(derivedSyncDirective); 11915 11916 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, 11917 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, 11918 previousCompileContext); 11919 forEach($rootElement, function(node, i) { 11920 if (node == compileNode) { 11921 $rootElement[i] = $compileNode[0]; 11922 } 11923 }); 11924 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); 11925 11926 while(linkQueue.length) { 11927 var scope = linkQueue.shift(), 11928 beforeTemplateLinkNode = linkQueue.shift(), 11929 linkRootElement = linkQueue.shift(), 11930 boundTranscludeFn = linkQueue.shift(), 11931 linkNode = $compileNode[0]; 11932 11933 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { 11934 var oldClasses = beforeTemplateLinkNode.className; 11935 11936 if (!(previousCompileContext.hasElementTranscludeDirective && 11937 origAsyncDirective.replace)) { 11938 // it was cloned therefore we have to clone as well. 11939 linkNode = jqLiteClone(compileNode); 11940 } 11941 11942 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); 11943 11944 // Copy in CSS classes from original node 11945 safeAddClass(jqLite(linkNode), oldClasses); 11946 } 11947 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { 11948 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); 11949 } else { 11950 childBoundTranscludeFn = boundTranscludeFn; 11951 } 11952 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, 11953 childBoundTranscludeFn); 11954 } 11955 linkQueue = null; 11956 }). 11957 error(function(response, code, headers, config) { 11958 throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url); 11959 }); 11960 11961 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { 11962 var childBoundTranscludeFn = boundTranscludeFn; 11963 if (linkQueue) { 11964 linkQueue.push(scope); 11965 linkQueue.push(node); 11966 linkQueue.push(rootElement); 11967 linkQueue.push(childBoundTranscludeFn); 11968 } else { 11969 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { 11970 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
11971 } 11972 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); 11973 } 11974 }; 11975 } 11976 11977 11978 /** 11979 * Sorting function for bound directives. 11980 */ 11981 function byPriority(a, b) { 11982 var diff = b.priority - a.priority; 11983 if (diff !== 0) return diff; 11984 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; 11985 return a.index - b.index; 11986 } 11987 11988 11989 function assertNoDuplicate(what, previousDirective, directive, element) { 11990 if (previousDirective) { 11991 throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}', 11992 previousDirective.name, directive.name, what, startingTag(element)); 11993 } 11994 } 11995 11996 11997 function addTextInterpolateDirective(directives, text) { 11998 var interpolateFn = $interpolate(text, true); 11999 if (interpolateFn) { 12000 directives.push({ 12001 priority: 0, 12002 compile: function textInterpolateCompileFn(templateNode) { 12003 // when transcluding a template that has bindings in the root 12004 // then we don't have a parent and should do this in the linkFn 12005 var parent = templateNode.parent(), hasCompileParent = parent.length; 12006 if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding'); 12007 12008 return function textInterpolateLinkFn(scope, node) { 12009 var parent = node.parent(), 12010 bindings = parent.data('$binding') || []; 12011 bindings.push(interpolateFn); 12012 parent.data('$binding', bindings); 12013 if (!hasCompileParent) safeAddClass(parent, 'ng-binding'); 12014 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { 12015 node[0].nodeValue = value; 12016 }); 12017 }; 12018 } 12019 }); 12020 } 12021 } 12022 12023 12024 function getTrustedContext(node, attrNormalizedName) { 12025 if (attrNormalizedName == "srcdoc") { 12026 return $sce.HTML; 12027 } 12028 var tag = nodeName_(node); 12029 // maction[xlink:href] can source SVG. It's not limited to <maction>. 12030 if (attrNormalizedName == "xlinkHref" || 12031 (tag == "FORM" && attrNormalizedName == "action") || 12032 (tag != "IMG" && (attrNormalizedName == "src" || 12033 attrNormalizedName == "ngSrc"))) { 12034 return $sce.RESOURCE_URL; 12035 } 12036 } 12037 12038 12039 function addAttrInterpolateDirective(node, directives, value, name) { 12040 var interpolateFn = $interpolate(value, true); 12041 12042 // no interpolation found -> ignore 12043 if (!interpolateFn) return; 12044 12045 12046 if (name === "multiple" && nodeName_(node) === "SELECT") { 12047 throw $compileMinErr("selmulti", 12048 "Binding to the 'multiple' attribute is not supported. Element: {0}", 12049 startingTag(node)); 12050 } 12051 12052 directives.push({ 12053 priority: 100, 12054 compile: function() { 12055 return { 12056 pre: function attrInterpolatePreLinkFn(scope, element, attr) { 12057 var $$observers = (attr.$$observers || (attr.$$observers = {})); 12058 12059 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { 12060 throw $compileMinErr('nodomevents', 12061 "Interpolations for HTML DOM event attributes are disallowed. Please use the " + 12062 "ng- versions (such as ng-click instead of onclick) instead."); 12063 } 12064 12065 // we need to interpolate again, in case the attribute value has been updated 12066 // (e.g. by another directive's compile function) 12067 interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name)); 12068 12069 // if attribute was updated so that there is no interpolation going on we don't want to 12070 // register any observers 12071 if (!interpolateFn) return; 12072 12073 // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the 12074 // actual attr value 12075 attr[name] = interpolateFn(scope); 12076 ($$observers[name] || ($$observers[name] = [])).$$inter = true; 12077 (attr.$$observers && attr.$$observers[name].$$scope || scope). 12078 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { 12079 //special case for class attribute addition + removal 12080 //so that class changes can tap into the animation 12081 //hooks provided by the $animate service. Be sure to 12082 //skip animations when the first digest occurs (when 12083 //both the new and the old values are the same) since 12084 //the CSS classes are the non-interpolated values 12085 if(name === 'class' && newValue != oldValue) { 12086 attr.$updateClass(newValue, oldValue); 12087 } else { 12088 attr.$set(name, newValue); 12089 } 12090 }); 12091 } 12092 }; 12093 } 12094 }); 12095 } 12096 12097 12098 /** 12099 * This is a special jqLite.replaceWith, which can replace items which 12100 * have no parents, provided that the containing jqLite collection is provided. 12101 * 12102 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes 12103 * in the root of the tree. 12104 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep 12105 * the shell, but replace its DOM node reference. 12106 * @param {Node} newNode The new DOM node. 12107 */ 12108 function replaceWith($rootElement, elementsToRemove, newNode) { 12109 var firstElementToRemove = elementsToRemove[0], 12110 removeCount = elementsToRemove.length, 12111 parent = firstElementToRemove.parentNode, 12112 i, ii; 12113 12114 if ($rootElement) { 12115 for(i = 0, ii = $rootElement.length; i < ii; i++) { 12116 if ($rootElement[i] == firstElementToRemove) { 12117 $rootElement[i++] = newNode; 12118 for (var j = i, j2 = j + removeCount - 1, 12119 jj = $rootElement.length; 12120 j < jj; j++, j2++) { 12121 if (j2 < jj) { 12122 $rootElement[j] = $rootElement[j2]; 12123 } else { 12124 delete $rootElement[j]; 12125 } 12126 } 12127 $rootElement.length -= removeCount - 1; 12128 break; 12129 } 12130 } 12131 } 12132 12133 if (parent) { 12134 parent.replaceChild(newNode, firstElementToRemove); 12135 } 12136 var fragment = document.createDocumentFragment(); 12137 fragment.appendChild(firstElementToRemove); 12138 newNode[jqLite.expando] = firstElementToRemove[jqLite.expando]; 12139 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { 12140 var element = elementsToRemove[k]; 12141 jqLite(element).remove(); // must do this way to clean up expando 12142 fragment.appendChild(element); 12143 delete elementsToRemove[k]; 12144 } 12145 12146 elementsToRemove[0] = newNode; 12147 elementsToRemove.length = 1; 12148 } 12149 12150 12151 function cloneAndAnnotateFn(fn, annotation) { 12152 return extend(function() { return fn.apply(null, arguments); }, fn, annotation); 12153 } 12154 }]; 12155} 12156 12157var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; 12158/** 12159 * Converts all accepted directives format into proper directive name. 12160 * All of these will become 'myDirective': 12161 * my:Directive 12162 * my-directive 12163 * x-my-directive 12164 * data-my:directive 12165 * 12166 * Also there is special case for Moz prefix starting with upper case letter. 12167 * @param name Name to normalize 12168 */ 12169function directiveNormalize(name) { 12170 return camelCase(name.replace(PREFIX_REGEXP, '')); 12171} 12172 12173/** 12174 * @ngdoc type 12175 * @name $compile.directive.Attributes 12176 * 12177 * @description 12178 * A shared object between directive compile / linking functions which contains
12178normalized DOM 12179 * element attributes. The values reflect current binding state `{{ }}`. The normalization is 12180 * needed since all of these are treated as equivalent in Angular: 12181 * 12182 * ``` 12183 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> 12184 * ``` 12185 */ 12186 12187/** 12188 * @ngdoc property 12189 * @name $compile.directive.Attributes#$attr 12190 * 12191 * @description 12192 * A map of DOM element attribute names to the normalized name. This is 12193 * needed to do reverse lookup from normalized name back to actual name. 12194 */ 12195 12196 12197/** 12198 * @ngdoc method 12199 * @name $compile.directive.Attributes#$set 12200 * @kind function 12201 * 12202 * @description 12203 * Set DOM element attribute value. 12204 * 12205 * 12206 * @param {string} name Normalized element attribute name of the property to modify. The name is 12207 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} 12208 * property to the original name. 12209 * @param {string} value Value to set the attribute to. The value can be an interpolated string. 12210 */ 12211 12212 12213 12214/** 12215 * Closure compiler type information 12216 */ 12217 12218function nodesetLinkingFn( 12219 /* angular.Scope */ scope, 12220 /* NodeList */ nodeList, 12221 /* Element */ rootElement, 12222 /* function(Function) */ boundTranscludeFn 12223){} 12224 12225function directiveLinkingFn( 12226 /* nodesetLinkingFn */ nodesetLinkingFn, 12227 /* angular.Scope */ scope, 12228 /* Node */ node, 12229 /* Element */ rootElement, 12230 /* function(Function) */ boundTranscludeFn 12231){} 12232 12233function tokenDifference(str1, str2) { 12234 var values = '', 12235 tokens1 = str1.split(/\s+/), 12236 tokens2 = str2.split(/\s+/); 12237 12238 outer: 12239 for(var i = 0; i < tokens1.length; i++) { 12240 var token = tokens1[i]; 12241 for(var j = 0; j < tokens2.length; j++) { 12242 if(token == tokens2[j]) continue outer; 12243 } 12244 values += (values.length > 0 ? ' ' : '') + token; 12245 } 12246 return values; 12247} 12248 12249/** 12250 * @ngdoc provider 12251 * @name $controllerProvider 12252 * @description 12253 * The {@link ng.$controller $controller service} is used by Angular to create new 12254 * controllers. 12255 * 12256 * This provider allows controller registration via the 12257 * {@link ng.$controllerProvider#register register} method. 12258 */ 12259function $ControllerProvider() { 12260 var controllers = {}, 12261 CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; 12262 12263 12264 /** 12265 * @ngdoc method 12266 * @name $controllerProvider#register 12267 * @param {string|Object} name Controller name, or an object map of controllers where the keys are 12268 * the names and the values are the constructors. 12269 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI 12270 * annotations in the array notation). 12271 */ 12272 this.register = function(name, constructor) { 12273 assertNotHasOwnProperty(name, 'controller'); 12274 if (isObject(name)) { 12275 extend(controllers, name); 12276 } else { 12277 controllers[name] = constructor; 12278 } 12279 }; 12280 12281 12282 this.$get = ['$injector', '$window', function($injector, $window) { 12283 12284 /** 12285 * @ngdoc service 12286 * @name $controller 12287 * @requires $injector 12288 * 12289 * @param {Function|string} constructor If called with a function then it's considered to be the 12290 * controller constructor function. Otherwise it's considered to be a string which is used 12291 * to retrieve the controller constructor using the following steps: 12292 * 12293 * * check if a controller with given name is registered via `$controllerProvider` 12294 * * check if evaluating the string on the current scope returns a constructor 12295 * * check `window[constructor]` on the global `window` object 12296 * 12297 * @param {Object} locals Injection locals for Controller. 12298 * @return {Object} Instance of given controller. 12299 * 12300 * @description 12301 * `$controller` service is responsible for instantiating controllers. 12302 * 12303 * It's just a simple call to {@link auto.$injector $injector}, but extracted into 12304 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). 12305 */ 12306 return function(expression, locals) { 12307 var instance, match, constructor, identifier; 12308 12309 if(isString(expression)) { 12310 match = expression.match(CNTRL_REG), 12311 constructor = match[1], 12312 identifier = match[3]; 12313 expression = controllers.hasOwnProperty(constructor) 12314 ? controllers[constructor] 12315 : getter(locals.$scope, constructor, true) || getter($window, constructor, true); 12316 12317 assertArgFn(expression, constructor, true); 12318 } 12319 12320 instance = $injector.instantiate(expression, locals); 12321 12322 if (identifier) { 12323 if (!(locals && typeof locals.$scope === 'object')) { 12324 throw minErr('$controller')('noscp', 12325 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", 12326 constructor || expression.name, identifier); 12327 } 12328 12329 locals.$scope[identifier] = instance; 12330 } 12331 12332 return instance; 12333 }; 12334 }]; 12335} 12336 12337/** 12338 * @ngdoc service 12339 * @name $document 12340 * @requires $window 12341 * 12342 * @description 12343 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. 12344 * 12345 * @example 12346 <example module="documentExample"> 12347 <file name="index.html">
12348 <div ng-controller="ExampleController"> 12349 <p>$document title: <b ng-bind="title"></b></p> 12350 <p>window.document title: <b ng-bind="windowTitle"></b></p> 12351 </div> 12352 </file> 12353 <file name="script.js"> 12354 angular.module('documentExample', []) 12355 .controller('ExampleController', ['$scope', '$document', function($scope, $document) { 12356 $scope.title = $document[0].title; 12357 $scope.windowTitle = angular.element(window.document)[0].title; 12358 }]); 12359 </file> 12360 </example> 12361 */ 12362function $DocumentProvider(){ 12363 this.$get = ['$window', function(window){ 12364 return jqLite(window.document); 12365 }]; 12366} 12367 12368/** 12369 * @ngdoc service 12370 * @name $exceptionHandler 12371 * @requires ng.$log 12372 * 12373 * @description 12374 * Any uncaught exception in angular expressions is delegated to this service. 12375 * The default implementation simply delegates to `$log.error` which logs it into 12376 * the browser console. 12377 * 12378 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by 12379 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. 12380 * 12381 * ## Example: 12382 * 12383 * ```js 12384 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function () { 12385 * return function (exception, cause) { 12386 * exception.message += ' (caused by "' + cause + '")'; 12387 * throw exception; 12388 * }; 12389 * }); 12390 * ``` 12391 * 12392 * This example will override the normal action of `$exceptionHandler`, to make angular 12393 * exceptions fail hard when they happen, instead of just logging to the console. 12394 * 12395 * @param {Error} exception Exception associated with the error. 12396 * @param {string=} cause optional information about the context in which 12397 * the error was thrown. 12398 * 12399 */ 12400function $ExceptionHandlerProvider() { 12401 this.$get = ['$log', function($log) { 12402 return function(exception, cause) { 12403 $log.error.apply($log, arguments); 12404 }; 12405 }]; 12406} 12407 12408/** 12409 * Parse headers into key value object 12410 * 12411 * @param {string} headers Raw headers as a string 12412 * @returns {Object} Parsed headers as key value object 12413 */ 12414function parseHeaders(headers) { 12415 var parsed = {}, key, val, i; 12416 12417 if (!headers) return parsed; 12418 12419 forEach(headers.split('\n'), function(line) { 12420 i = line.indexOf(':');
12421 key = lowercase(trim(line.substr(0, i))); 12422 val = trim(line.substr(i + 1)); 12423 12424 if (key) { 12425 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 12426 } 12427 }); 12428 12429 return parsed; 12430} 12431 12432 12433/** 12434 * Returns a function that provides access to parsed headers. 12435 * 12436 * Headers are lazy parsed when first requested. 12437 * @see parseHeaders 12438 * 12439 * @param {(string|Object)} headers Headers to provide access to. 12440 * @returns {function(string=)} Returns a getter function which if called with: 12441 * 12442 * - if called with single an argument returns a single header value or null 12443 * - if called with no arguments returns an object containing all headers. 12444 */ 12445function headersGetter(headers) { 12446 var headersObj = isObject(headers) ? headers : undefined; 12447 12448 return function(name) { 12449 if (!headersObj) headersObj = parseHeaders(headers); 12450 12451 if (name) { 12452 return headersObj[lowercase(name)] || null; 12453 } 12454 12455 return headersObj; 12456 }; 12457} 12458 12459 12460/** 12461 * Chain all given functions 12462 * 12463 * This function is used for both request and response transforming 12464 * 12465 * @param {*} data Data to transform. 12466 * @param {function(string=)} headers Http headers getter fn. 12467 * @param {(Function|Array.<Function>)} fns Function or an array of functions. 12468 * @returns {*} Transformed data. 12469 */ 12470function transformData(data, headers, fns) { 12471 if (isFunction(fns)) 12472 return fns(data, headers); 12473 12474 forEach(fns, function(fn) { 12475 data = fn(data, headers); 12476 }); 12477 12478 return data; 12479} 12480 12481 12482function isSuccess(status) { 12483 return 200 <= status && status < 300; 12484} 12485 12486 12487/** 12488 * @ngdoc provider 12489 * @name $httpProvider 12490 * @description 12491 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. 12492 * */ 12493function $HttpProvider() { 12494 var JSON_START = /^\s*(\[|\{[^\{])/, 12495 JSON_END = /[\}\]]\s*$/, 12496 PROTECTION_PREFIX = /^\)\]\}',?\n/, 12497 CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'}; 12498 12499 /** 12500 * @ngdoc property 12501 * @name $httpProvider#defaults 12502 * @description 12503 * 12504 * Object containing default values for all {@link ng.$http $http} requests. 12505 * 12506 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. 12507 * Defaults value is `'XSRF-TOKEN'`. 12508 * 12509 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the 12510 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. 12511 * 12512 * - **`defaults.headers`** - {Object} - Default headers for all $http requests. 12513 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on 12514 * setting default headers. 12515 * - **`defaults.headers.common`** 12516 * - **`defaults.headers.post`** 12517 * - **`defaults.headers.put`** 12518 * - **`defaults.headers.patch`** 12519 **/ 12520 var defaults = this.defaults = { 12521 // transform incoming response data 12522 transformResponse: [function(data) { 12523 if (isString(data)) { 12524 // strip json vulnerability protection prefix 12525 data = data.replace(PROTECTION_PREFIX, ''); 12526 if (JSON_START.test(data) && JSON_END.test(data)) 12527 data = fromJson(data); 12528 } 12529 return data; 12530 }], 12531 12532 // transform outgoing request data 12533 transformRequest: [function(d) { 12534 return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d; 12535 }], 12536 12537 // default headers 12538 headers: { 12539 common: { 12540 'Accept': 'application/json, text/plain, */*' 12541 }, 12542 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), 12543 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), 12544 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) 12545 }, 12546 12547 xsrfCookieName: 'XSRF-TOKEN', 12548 xsrfHeaderName: 'X-XSRF-TOKEN' 12549 }; 12550 12551 /** 12552 * Are ordered by request, i.e. they are applied in the same order as the 12553 * array, on request, but reverse order, on response. 12554 */ 12555 var interceptorFactories = this.interceptors = []; 12556 12557 /** 12558 * For historical reasons, response interceptors are ordered by the order in which 12559 * they are applied to the response. (This is the opposite of interceptorFactories) 12560 */ 12561 var responseInterceptorFactories = this.responseInterceptors = []; 12562 12563 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', 12564 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { 12565 12566 var defaultCache = $cacheFactory('$http'); 12567 12568 /** 12569 * Interceptors stored in reverse order. Inner interceptors before outer interceptors. 12570 * The reversal is needed so that we can build up the interception chain around the 12571 * server request. 12572 */ 12573 var reversedInterceptors = []; 12574
12575 forEach(interceptorFactories, function(interceptorFactory) { 12576 reversedInterceptors.unshift(isString(interceptorFactory) 12577 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); 12578 }); 12579 12580 forEach(responseInterceptorFactories, function(interceptorFactory, index) { 12581 var responseFn = isString(interceptorFactory) 12582 ? $injector.get(interceptorFactory) 12583 : $injector.invoke(interceptorFactory); 12584 12585 /** 12586 * Response interceptors go before "around" interceptors (no real reason, just 12587 * had to pick one.) But they are already reversed, so we can't use unshift, hence 12588 * the splice. 12589 */ 12590 reversedInterceptors.splice(index, 0, { 12591 response: function(response) { 12592 return responseFn($q.when(response)); 12593 }, 12594 responseError: function(response) { 12595 return responseFn($q.reject(response)); 12596 } 12597 }); 12598 }); 12599 12600 12601 /** 12602 * @ngdoc service 12603 * @kind function 12604 * @name $http 12605 * @requires ng.$httpBackend 12606 * @requires $cacheFactory 12607 * @requires $rootScope 12608 * @requires $q 12609 * @requires $injector 12610 * 12611 * @description 12612 * The `$http` service is a core Angular service that facilitates communication with the remote 12613 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) 12614 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). 12615 * 12616 * For unit testing applications that use `$http` service, see 12617 * {@link ngMock.$httpBackend $httpBackend mock}. 12618 * 12619 * For a higher level of abstraction, please check out the {@link ngResource.$resource 12620 * $resource} service. 12621 * 12622 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by 12623 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage 12624 * it is important to familiarize yourself with these APIs and the guarantees they provide. 12625 * 12626 * 12627 * # General usage 12628 * The `$http` service is a function which takes a single argument â a configuration object â 12629 * that is used to generate an HTTP request and returns a {@link ng.$q promise} 12630 * with two $http specific methods: `success` and `error`. 12631 * 12632 * ```js 12633 * $http({method: 'GET', url: '/someUrl'}). 12634 * success(function(data, status, headers, config) { 12635 * // this callback will be called asynchronously 12636 * // when the response is available 12637 * }). 12638 * error(function(data, status, headers, config) { 12639 * // called asynchronously if an error occurs 12640 * // or server returns response with an error status. 12641 * }); 12642 * ``` 12643 * 12644 * Since the returned value of calling the $http function is a `promise`, you can also use 12645 * the `then` method to register callbacks, and these callbacks will receive a single argument â 12646 * an object representing the response. See the API signature and type info below for more 12647 * details. 12648 * 12649 * A response status code between 200 and 299 is considered a success status and 12650 * will result in the success callback being called. Note that if the response is a redirect, 12651 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be 12652 * called for such responses. 12653 * 12654 * # Writing Unit Tests that use $http 12655 * When unit testing (using {@link ngMock ngMock}), it is necessary to call 12656 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending 12657 * request using trained responses. 12658 * 12659 * ``` 12660 * $httpBackend.expectGET(...); 12661 * $http.get(...); 12662 * $httpBackend.flush(); 12663 * ``` 12664 * 12665 * # Shortcut methods 12666 * 12667 * Shortcut methods are also available. All shortcut methods require passing in the URL, and 12668 * request data must be passed in for POST/PUT requests. 12669 * 12670 * ```js 12671 * $http.get('/someUrl').success(successCallback); 12672 * $http.post('/someUrl', data).success(successCallback); 12673 * ``` 12674 * 12675 * Complete list of shortcut methods: 12676 * 12677 * - {@link ng.$http#get $http.get} 12678 * - {@link ng.$http#head $http.head} 12679 * - {@link ng.$http#post $http.post} 12680 * - {@link ng.$http#put $http.put} 12681 * - {@link ng.$http#delete $http.delete} 12682 * - {@link ng.$http#jsonp $http.jsonp} 12683 * - {@link ng.$http#patch $http.patch} 12684 * 12685 * 12686 * # Setting HTTP Headers 12687 * 12688 * The $http service will automatically add certain HTTP headers to all requests. These defaults 12689 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration 12690 * object, which currently contains this default configuration: 12691 * 12692 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): 12693 * - `Accept: application/json, text/plain, * / *` 12694 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) 12695 * - `Content-Type: application/json` 12696 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) 12697 * - `Content-Type: application/json` 12698 *
12699 * To add or overwrite these defaults, simply add or remove a property from these configuration 12700 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object 12701 * with the lowercased HTTP method name as the key, e.g. 12702 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }. 12703 * 12704 * The defaults can also be set at runtime via the `$http.defaults` object in the same 12705 * fashion. For example: 12706 * 12707 * ``` 12708 * module.run(function($http) { 12709 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' 12710 * }); 12711 * ``` 12712 * 12713 * In addition, you can supply a `headers` property in the config object passed when 12714 * calling `$http(config)`, which overrides the defaults without changing them globally. 12715 * 12716 * 12717 * # Transforming Requests and Responses 12718 * 12719 * Both requests and responses can be transformed using transform functions. By default, Angular 12720 * applies these transformations: 12721 * 12722 * Request transformations: 12723 * 12724 * - If the `data` property of the request configuration object contains an object, serialize it 12725 * into JSON format. 12726 * 12727 * Response transformations: 12728 * 12729 * - If XSRF prefix is detected, strip it (see Security Considerations section below). 12730 * - If JSON response is detected, deserialize it using a JSON parser. 12731 * 12732 * To globally augment or override the default transforms, modify the 12733 * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse` 12734 * properties. These properties are by default an array of transform functions, which allows you 12735 * to `push` or `unshift` a new transformation function into the transformation chain. You can 12736 * also decide to completely override any default transformations by assigning your 12737 * transformation functions to these properties directly without the array wrapper. These defaults 12738 * are again available on the $http factory at run-time, which may be useful if you have run-time 12739 * services you wish to be involved in your transformations. 12740 * 12741 * Similarly, to locally override the request/response transforms, augment the 12742 * `transformRequest` and/or `transformResponse` properties of the configuration object passed 12743 * into `$http`. 12744 * 12745 * 12746 * # Caching 12747 * 12748 * To enable caching, set the request configuration `cache` property to `true` (to use default 12749 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). 12750 * When the cache is enabled, `$http` stores the response from the server in the specified 12751 * cache. The next time the same request is made, the response is served from the cache without 12752 * sending a request to the server. 12753 * 12754 * Note that even if the response is served from cache, delivery of the data is asynchronous in 12755 * the same way that real requests are. 12756 * 12757 * If there are multiple GET requests for the same URL that should be cached using the same 12758 * cache, but the cache is not populated yet, only one request to the server will be made and 12759 * the remaining requests will be fulfilled using the response from the first request. 12760 * 12761 * You can change the default cache to a new object (built with 12762 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the 12763 * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set 12764 * their `cache` property to `true` will now use this cache object. 12765 * 12766 * If you set the default cache to `false` then only requests that specify their own custom 12767 * cache object will be cached. 12768 * 12769 * # Interceptors 12770 * 12771 * Before you start creating interceptors, be sure to understand the 12772 * {@link ng.$q $q and deferred/promise APIs}. 12773 * 12774 * For purposes of global error handling, authentication, or any kind of synchronous or 12775 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be 12776 * able to intercept requests before they are handed to the server and 12777 * responses before they are handed over to the application code that 12778 * initiated these requests. The interceptors leverage the {@link ng.$q 12779 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. 12780 * 12781 * The interceptors are service factories that are registered with the `$httpProvider` by 12782 * adding them to the `$httpProvider.interceptors` array. The factory is called and 12783 * injected with dependencies (if specified) and returns the interceptor. 12784 * 12785 * There are two kinds of interceptors (and two kinds of rejection interceptors): 12786 * 12787 * * `request`: interceptors get called with a http `config` object. The function is free to 12788 * modify the `config` object or create a new one. The function needs to return the `config` 12789 * object directly, or a promise containing the `config` or a new `config` object. 12790 * * `requestError`: interceptor gets called when a previous interceptor threw an error or 12791 * resolved with a rejection. 12792 * * `response`: interceptors get called with http `response` object. The function is free to 12793 * modify the `response` object or create a new one. The function needs to return the `response` 12794 * object directly, or as a promise containing the `response` or a new `response` object. 12795 * * `responseError`: interceptor gets called when a previous interceptor threw an error or 12796 * resolved with a rejection. 12797 * 12798 * 12799 * ```js 12800 * // register the interceptor as a service 12801 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { 12802 * return { 12803 * // optional method 12804 * 'request': function(config) {
12805 * // do something on success 12806 * return config; 12807 * }, 12808 * 12809 * // optional method 12810 * 'requestError': function(rejection) { 12811 * // do something on error 12812 * if (canRecover(rejection)) { 12813 * return responseOrNewPromise 12814 * } 12815 * return $q.reject(rejection); 12816 * }, 12817 * 12818 * 12819 * 12820 * // optional method 12821 * 'response': function(response) { 12822 * // do something on success 12823 * return response; 12824 * }, 12825 * 12826 * // optional method 12827 * 'responseError': function(rejection) { 12828 * // do something on error 12829 * if (canRecover(rejection)) { 12830 * return responseOrNewPromise 12831 * } 12832 * return $q.reject(rejection); 12833 * } 12834 * }; 12835 * }); 12836 * 12837 * $httpProvider.interceptors.push('myHttpInterceptor'); 12838 * 12839 * 12840 * // alternatively, register the interceptor via an anonymous factory 12841 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { 12842 * return { 12843 * 'request': function(config) { 12844 * // same as above 12845 * }, 12846 * 12847 * 'response': function(response) { 12848 * // same as above 12849 * } 12850 * }; 12851 * }); 12852 * ``` 12853 * 12854 * # Response interceptors (DEPRECATED) 12855 * 12856 * Before you start creating interceptors, be sure to understand the 12857 * {@link ng.$q $q and deferred/promise APIs}. 12858 * 12859 * For purposes of global error handling, authentication or any kind of synchronous or 12860 * asynchronous preprocessing of received responses, it is desirable to be able to intercept 12861 * responses for http requests before they are handed over to the application code that 12862 * initiated these requests. The response interceptors leverage the {@link ng.$q 12863 * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. 12864 * 12865 * The interceptors are service factories that are registered with the $httpProvider by 12866 * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and 12867 * injected with dependencies (if specified) and returns the interceptor â a function that 12868 * takes a {@link ng.$q promise} and returns the original or a new promise. 12869 * 12870 * ```js 12871 * // register the interceptor as a service 12872 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { 12873 * return function(promise) { 12874 * return promise.then(function(response) { 12875 * // do something on success 12876 * return response; 12877 * }, function(response) { 12878 * // do something on error 12879 * if (canRecover(response)) { 12880 * return responseOrNewPromise 12881 * } 12882 * return $q.reject(response); 12883 * }); 12884 * } 12885 * }); 12886 * 12887 * $httpProvider.responseInterceptors.push('myHttpInterceptor'); 12888 * 12889 * 12890 * // register the interceptor via an anonymous factory 12891 * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) { 12892 * return function(promise) { 12893 * // same as above 12894 * } 12895 * }); 12896 * ``` 12897 * 12898 * 12899 * # Security Considerations 12900 * 12901 * When designing web applications, consider security threats from: 12902 * 12903 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) 12904 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) 12905 * 12906 * Both server and the client must cooperate in order to eliminate these threats. Angular comes 12907 * pre-configured with strategies that address these issues, but for this to work backend server 12908 * cooperation is required. 12909 * 12910 * ## JSON Vulnerability Protection 12911 * 12912 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) 12913 * allows third party website to turn your JSON resource URL into 12914 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To 12915 * counter this your server can prefix all JSON requests with following str
12915ing `")]}',\n"`. 12916 * Angular will automatically strip the prefix before processing it as JSON. 12917 * 12918 * For example if your server needs to return: 12919 * ```js 12920 * ['one','two'] 12921 * ``` 12922 * 12923 * which is vulnerable to attack, your server can return: 12924 * ```js 12925 * )]}', 12926 * ['one','two'] 12927 * ``` 12928 * 12929 * Angular will strip the prefix, before processing the JSON. 12930 * 12931 * 12932 * ## Cross Site Request Forgery (XSRF) Protection 12933 * 12934 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which 12935 * an unauthorized site can gain your user's private data. Angular provides a mechanism 12936 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie 12937 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only 12938 * JavaScript that runs on your domain could read the cookie, your server can be assured that 12939 * the XHR came from JavaScript running on your domain. The header will not be set for 12940 * cross-domain requests. 12941 * 12942 * To take advantage of this, your server needs to set a token in a JavaScript readable session 12943 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the 12944 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure 12945 * that only JavaScript running on your domain could have sent the request. The token must be 12946 * unique for each user and must be verifiable by the server (to prevent the JavaScript from 12947 * making up its own tokens). We recommend that the token is a digest of your site's 12948 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) 12949 * for added security. 12950 * 12951 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName 12952 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, 12953 * or the per-request config object. 12954 * 12955 * 12956 * @param {object} config Object describing the request to be made and how it should be 12957 * processed. The object has following properties: 12958 * 12959 * - **method** â `{string}` â HTTP method (e.g. 'GET', 'POST', etc) 12960 * - **url** â `{string}` â Absolute or relative URL of the resource that is being requested. 12961 * - **params** â `{Object.<string|Object>}` â Map of strings or objects which will be turned 12962 * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be 12963 * JSONified. 12964 * - **data** â `{string|Object}` â Data to be sent as the request message data. 12965 * - **headers** â `{Object}` â Map of strings or functions which return strings representing 12966 * HTTP headers to send to the server. If the return value of a function is null, the 12967 * header will not be sent. 12968 * - **xsrfHeaderName** â `{string}` â Name of HTTP header to populate with the XSRF token. 12969 * - **xsrfCookieName** â `{string}` â Name of cookie containing the XSRF token. 12970 * - **transformRequest** â 12971 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â 12972 * transform function or an array of such functions. The transform function takes the http 12973 * request body and headers and returns its transformed (typically serialized) version. 12974 * - **transformResponse** â 12975 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` â 12976 * transform function or an array of such functions. The transform function takes the http 12977 * response body and headers and returns its transformed (typically deserialized) version. 12978 * - **cache** â `{boolean|Cache}` â If true, a default $http cache will be used to cache the 12979 * GET request, otherwise if a cache instance built with 12980 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for 12981 * caching. 12982 * - **timeout** â `{number|Promise}` â timeout in milliseconds, or {@link ng.$q promise} 12983 * that should abort the request when resolved. 12984 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the 12985 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) 12986 * for more information. 12987 * - **responseType** - `{string}` - see 12988 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). 12989 * 12990 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the 12991 * standard `then` method and two http specific methods: `success` and `error`. The `then` 12992 * method takes two arguments a success and an error callback which will be called with a 12993 * response object. The `success` and `error` methods take a single argument - a function that 12994 * will be called when the request succeeds or fails respectively. The arguments passed into 12995 * these functions are destructured representation of the response object passed into the 12996 * `then` method. The response object has these properties: 12997 * 12998 * - **data** â `{string|Object}` â The response body transformed with the transform 12999 * functions. 13000 * - **status** â `{number}` â HTTP status code of the response. 13001 * - **headers** â `{function([headerName])}` â Header getter function. 13002 * - **config** â `{Object}` â The configuration object that was used to generate the request. 13003 * - **statusText** â `{string}` â HTTP status text of the response. 13004 * 13005 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending 13006 * requests. This is primarily meant to be used for debugging purposes. 13007 * 13008 * 13009 * @example 13010<example module="httpExample"> 13011<file name="index.html"> 13012 <div ng-controller="FetchController"> 13013 <select ng-model="method"> 13014 <option>GET</option> 13015 <option>JSONP</option> 13016 </select> 13017 <input type="text" ng-model="url" size="80"/> 13018 <button id="fetchbtn" ng-click="fetch()">fetch</button><br> 13019 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> 13020 <button id="samplejsonpbtn" 13021 ng-click="updateModel('JSONP', 13022 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')"> 13023 Sample JSONP 13024 </button> 13025 <button id="invalidjsonpbtn" 13026 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')"> 13027 Invalid JSONP 13028 </button> 13029 <pre>http status code: {{status}}</pre> 13030 <pre>http response data: {{data}}</pre> 13031 </div> 13032</file> 13033<file name="script.js"> 13034 angular.module('httpExample', []) 13035 .controller('FetchController', ['$scope', '$http', '$templateCache', 13036 function($scope, $http, $templateCache) { 13037 $scope.method = 'GET'; 13038 $scope.url = 'http-hello.html'; 13039 13040 $scope.fetch = function() { 13041 $scope.code = null; 13042 $scope.response = null; 13043 13044 $http({method: $scope.method, url: $scope.url, cache: $templateCache}). 13045 success(function(data, status) { 13046 $scope.status = status; 13047 $scope.data = data; 13048 }). 13049 error(function(data, status) { 13050 $scope.data = data || "Request failed"; 13051 $scope.status = status; 13052 }); 13053 }; 13054 13055 $scope.updateModel = function(method, url) { 13056 $scope.method = method; 13057 $scope.url = url; 13058 }; 13059 }]); 13060</file> 13061<file name="http-hello.html"> 13062 Hello, $http! 13063</file> 13064<file name="protractor.js" type="protractor"> 13065 var status = element(by.binding('status')); 13066 var data = element(by.binding('data')); 13067 var fetchBtn = element(by.id('fetchbtn')); 13068 var sampleGetBtn = element(by.id('samplegetbtn')); 13069 var sampleJsonpBtn = element(by.id('samplejsonpbtn')); 13070 var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); 13071 13072 it('should make an xhr GET request', function() { 13073 sampleGetBtn.click(); 13074 fetchBtn.click(); 13075 expect(status.getText()).toMatch('200'); 13076 expect(data.getText()).toMatch(/Hello, \$http!/); 13077 }); 13078 13079 it('should make a JSONP request to angularjs.org', function() { 13080 sampleJsonpBtn.click(); 13081 fetchBtn.click(); 13082 expect(status.getText()).toMatch('200'); 13083 expect(data.getText()).toMatch(/Super Hero!/); 13084 }); 13085
13086 it('should make JSONP request to invalid URL and invoke the error handler', 13087 function() { 13088 invalidJsonpBtn.click(); 13089 fetchBtn.click(); 13090 expect(status.getText()).toMatch('0'); 13091 expect(data.getText()).toMatch('Request failed'); 13092 }); 13093</file> 13094</example> 13095 */ 13096 function $http(requestConfig) { 13097 var config = { 13098 method: 'get', 13099 transformRequest: defaults.transformRequest, 13100 transformResponse: defaults.transformResponse 13101 }; 13102 var headers = mergeHeaders(requestConfig); 13103 13104 extend(config, requestConfig); 13105 config.headers = headers; 13106 config.method = uppercase(config.method); 13107 13108 var serverRequest = function(config) { 13109 headers = config.headers; 13110 var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); 13111 13112 // strip content-type if data is undefined 13113 if (isUndefined(reqData)) { 13114 forEach(headers, function(value, header) { 13115 if (lowercase(header) === 'content-type') { 13116 delete headers[header]; 13117 } 13118 }); 13119 } 13120 13121 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { 13122 config.withCredentials = defaults.withCredentials; 13123 } 13124 13125 // send request 13126 return sendReq(config, reqData, headers).then(transformResponse, transformResponse); 13127 }; 13128 13129 var chain = [serverRequest, undefined]; 13130 var promise = $q.when(config); 13131 13132 // apply interceptors 13133 forEach(reversedInterceptors, function(interceptor) { 13134 if (interceptor.request || interceptor.requestError) { 13135 chain.unshift(interceptor.request, interceptor.requestError); 13136 } 13137 if (interceptor.response || interceptor.responseError) { 13138 chain.push(interceptor.response, interceptor.responseError); 13139 } 13140 }); 13141 13142 while(chain.length) { 13143 var thenFn = chain.shift(); 13144 var rejectFn = chain.shift(); 13145 13146 promise = promise.then(thenFn, rejectFn); 13147 } 13148 13149 promise.success = function(fn) { 13150 promise.then(function(response) { 13151 fn(response.data, response.status, response.headers, config); 13152 }); 13153 return promise; 13154 }; 13155 13156 promise.error = function(fn) { 13157 promise.then(null, function(response) { 13158 fn(response.data, response.status, response.headers, config); 13159 }); 13160 return promise; 13161 }; 13162 13163 return promise; 13164 13165 function transformResponse(response) { 13166 // make a copy since the response must be cacheable 13167 var resp = extend({}, response, { 13168 data: transformData(response.data, response.headers, config.transformResponse) 13169 }); 13170 return (isSuccess(response.status)) 13171 ? resp 13172 : $q.reject(resp); 13173 } 13174 13175 function mergeHeaders(config) { 13176 var defHeaders = defaults.headers, 13177 reqHeaders = extend({}, config.headers), 13178 defHeaderName, lowercaseDefHeaderName, reqHeaderName; 13179 13180 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); 13181 13182 // using for-in instead of forEach to avoid unecessary iteration after header has been found 13183 defaultHeadersIteration: 13184 for (defHeaderName in defHeaders) { 13185 lowercaseDefHeaderName = lowercase(defHeaderName); 13186 13187 for (reqHeaderName in reqHeaders) { 13188 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { 13189 continue defaultHeadersIteration; 13190 } 13191 } 13192 13193 reqHeaders[defHeaderName] = defHeaders[defHeaderName]; 13194 } 13195 13196 // execute if header value is a function for merged headers 13197 execHeaders(reqHeaders); 13198 return reqHeaders; 13199 13200 function execHeaders(headers) { 13201 var headerContent; 13202 13203 forEach(headers, function(headerFn, header) { 13204 if (isFunction(headerFn)) { 13205 headerContent = headerFn(); 13206 if (headerContent != null) { 13207 headers[header] = headerContent; 13208 } else { 13209 delete headers[header]; 13210 } 13211 } 13212 }); 13213 } 13214 } 13215 } 13216 13217 $http.pendingRequests = []; 13218 13219 /** 13220 * @ngdoc method 13221 * @name $http#get 13222 *
13223 * @description 13224 * Shortcut method to perform `GET` request. 13225 * 13226 * @param {string} url Relative or absolute URL specifying the destination of the request 13227 * @param {Object=} config Optional configuration object 13228 * @returns {HttpPromise} Future object 13229 */ 13230 13231 /** 13232 * @ngdoc method 13233 * @name $http#delete 13234 * 13235 * @description 13236 * Shortcut method to perform `DELETE` request. 13237 * 13238 * @param {string} url Relative or absolute URL specifying the destination of the request 13239 * @param {Object=} config Optional configuration object 13240 * @returns {HttpPromise} Future object 13241 */ 13242 13243 /** 13244 * @ngdoc method 13245 * @name $http#head 13246 * 13247 * @description 13248 * Shortcut method to perform `HEAD` request. 13249 * 13250 * @param {string} url Relative or absolute URL specifying the destination of the request 13251 * @param {Object=} config Optional configuration object 13252 * @returns {HttpPromise} Future object 13253 */ 13254 13255 /** 13256 * @ngdoc method 13257 * @name $http#jsonp 13258 * 13259 * @description 13260 * Shortcut method to perform `JSONP` request. 13261 * 13262 * @param {string} url Relative or absolute URL specifying the destination of the request. 13263 * The name of the callback should be the string `JSON_CALLBACK`. 13264 * @param {Object=} config Optional configuration object 13265 * @returns {HttpPromise} Future object 13266 */ 13267 createShortMethods('get', 'delete', 'head', 'jsonp'); 13268 13269 /** 13270 * @ngdoc method 13271 * @name $http#post 13272 * 13273 * @description 13274 * Shortcut method to perform `POST` request. 13275 * 13276 * @param {string} url Relative or absolute URL specifying the destination of the request 13277 * @param {*} data Request content 13278 * @param {Object=} config Optional configuration object 13279 * @returns {HttpPromise} Future object 13280 */ 13281 13282 /** 13283 * @ngdoc method 13284 * @name $http#put 13285 * 13286 * @description 13287 * Shortcut method to perform `PUT` request. 13288 * 13289 * @param {string} url Relative or absolute URL specifying the destination of the request 13290 * @param {*} data Request content 13291 * @param {Object=} config Optional configuration object 13292 * @returns {HttpPromise} Future object 13293 */ 13294 createShortMethodsWithData('post', 'put'); 13295 13296 /** 13297 * @ngdoc property 13298 * @name $http#defaults 13299 * 13300 * @description 13301 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of 13302 * default headers, withCredentials as well as request and response transformations. 13303 * 13304 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. 13305 */ 13306 $http.defaults = defaults; 13307 13308 13309 return $http; 13310 13311 13312 function createShortMethods(names) { 13313 forEach(arguments, function(name) { 13314 $http[name] = function(url, config) { 13315 return $http(extend(config || {}, { 13316 method: name, 13317 url: url 13318 })); 13319 }; 13320 }); 13321 } 13322 13323 13324 function createShortMethodsWithData(name) { 13325 forEach(arguments, function(name) { 13326 $http[name] = function(url, data, config) { 13327 return $http(extend(config || {}, { 13328 method: name, 13329 url: url, 13330 data: data 13331 })); 13332 }; 13333 }); 13334 } 13335 13336 13337 /** 13338 * Makes the request. 13339 * 13340 * !!! ACCESSES CLOSURE VARS: 13341 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests 13342 */ 13343 function sendReq(config, reqData, reqHeaders) { 13344 var deferred = $q.defer(), 13345 promise = deferred.promise, 13346 cache, 13347 cachedResp, 13348 url = buildUrl(config.url, config.params); 13349 13350 $http.pendingRequests.push(config); 13351 promise.then(removePendingReq, removePendingReq); 13352 13353 13354 if ((config.cache || defaults.cache) && config.cache !== false && 13355 (config.method === 'GET' || config.method === 'JSONP')) { 13356 cache = isObject(config.cache) ? config.cache 13357 : isObject(defaults.cache) ? defaults.cache 13358 : defaultCache; 13359 } 13360 13361 if (cache) { 13362 cachedResp = cache.get(url); 13363 if (isDefined(cachedResp)) {
13364 if (isPromiseLike(cachedResp)) { 13365 // cached request has already been sent, but there is no response yet 13366 cachedResp.then(removePendingReq, removePendingReq); 13367 return cachedResp; 13368 } else { 13369 // serving from cache 13370 if (isArray(cachedResp)) { 13371 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); 13372 } else { 13373 resolvePromise(cachedResp, 200, {}, 'OK'); 13374 } 13375 } 13376 } else { 13377 // put the promise for the non-transformed response into cache as a placeholder 13378 cache.put(url, promise); 13379 } 13380 } 13381 13382 13383 // if we won't have the response in cache, set the xsrf headers and 13384 // send the request to the backend 13385 if (isUndefined(cachedResp)) { 13386 var xsrfValue = urlIsSameOrigin(config.url) 13387 ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] 13388 : undefined; 13389 if (xsrfValue) { 13390 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; 13391 } 13392 13393 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, 13394 config.withCredentials, config.responseType); 13395 } 13396 13397 return promise; 13398 13399 13400 /** 13401 * Callback registered to $httpBackend(): 13402 * - caches the response if desired 13403 * - resolves the raw $http promise 13404 * - calls $apply 13405 */ 13406 function done(status, response, headersString, statusText) { 13407 if (cache) { 13408 if (isSuccess(status)) { 13409 cache.put(url, [status, response, parseHeaders(headersString), statusText]); 13410 } else { 13411 // remove promise from the cache 13412 cache.remove(url); 13413 } 13414 } 13415 13416 resolvePromise(response, status, headersString, statusText); 13417 if (!$rootScope.$$phase) $rootScope.$apply(); 13418 } 13419 13420 13421 /** 13422 * Resolves the raw $http promise. 13423 */ 13424 function resolvePromise(response, status, headers, statusText) { 13425 // normalize internal statuses to 0 13426 status = Math.max(status, 0); 13427 13428 (isSuccess(status) ? deferred.resolve : deferred.reject)({ 13429 data: response, 13430 status: status, 13431 headers: headersGetter(headers), 13432 config: config, 13433 statusText : statusText 13434 }); 13435 } 13436 13437 13438 function removePendingReq() { 13439 var idx = indexOf($http.pendingRequests, config); 13440 if (idx !== -1) $http.pendingRequests.splice(idx, 1); 13441 } 13442 } 13443 13444 13445 function buildUrl(url, params) { 13446 if (!params) return url; 13447 var parts = []; 13448 forEachSorted(params, function(value, key) { 13449 if (value === null || isUndefined(value)) return; 13450 if (!isArray(value)) value = [value]; 13451 13452 forEach(value, function(v) { 13453 if (isObject(v)) { 13454 if (isDate(v)){ 13455 v = v.toISOString(); 13456 } else { 13457 v = toJson(v); 13458 } 13459 } 13460 parts.push(encodeUriQuery(key) + '=' + 13461 encodeUriQuery(v)); 13462 }); 13463 }); 13464 if(parts.length > 0) { 13465 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); 13466 } 13467 return url; 13468 } 13469 }]; 13470} 13471 13472function createXhr(method) { 13473 //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest 13474 //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest 13475 //if it is available 13476 if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) || 13477 !window.XMLHttpRequest)) { 13478 return new window.ActiveXObject("Microsoft.XMLHTTP"); 13479 } else if (window.XMLHttpRequest) { 13480 return new window.XMLHttpRequest(); 13481 } 13482 13483 throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest."); 13484} 13485 13486/** 13487 * @ngdoc service 13488 * @name $httpBackend 13489 * @requires $window 13490 * @requires $document 13491 * 13492 * @description 13493 * HTTP backend used by the {@link ng.$http service} that delegates to
13494 * XMLHttpRequest object or JSONP and deals with browser incompatibilities. 13495 * 13496 * You should never need to use this service directly, instead use the higher-level abstractions: 13497 * {@link ng.$http $http} or {@link ngResource.$resource $resource}. 13498 * 13499 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock 13500 * $httpBackend} which can be trained with responses. 13501 */ 13502function $HttpBackendProvider() { 13503 this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { 13504 return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); 13505 }]; 13506} 13507 13508function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { 13509 var ABORTED = -1; 13510 13511 // TODO(vojta): fix the signature 13512 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { 13513 var status; 13514 $browser.$$incOutstandingRequestCount(); 13515 url = url || $browser.url(); 13516 13517 if (lowercase(method) == 'jsonp') { 13518 var callbackId = '_' + (callbacks.counter++).toString(36); 13519 callbacks[callbackId] = function(data) { 13520 callbacks[callbackId].data = data; 13521 callbacks[callbackId].called = true; 13522 }; 13523 13524 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), 13525 callbackId, function(status, text) { 13526 completeRequest(callback, status, callbacks[callbackId].data, "", text); 13527 callbacks[callbackId] = noop; 13528 }); 13529 } else { 13530 13531 var xhr = createXhr(method); 13532 13533 xhr.open(method, url, true); 13534 forEach(headers, function(value, key) { 13535 if (isDefined(value)) { 13536 xhr.setRequestHeader(key, value); 13537 } 13538 }); 13539 13540 // In IE6 and 7, this might be called synchronously when xhr.send below is called and the 13541 // response is in the cache. the promise api will ensure that to the app code the api is 13542 // always async 13543 xhr.onreadystatechange = function() { 13544 // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by 13545 // xhrs that are resolved while the app is in the background (see #5426). 13546 // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before 13547 // continuing 13548 // 13549 // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and 13550 // Safari respectively. 13551 if (xhr && xhr.readyState == 4) { 13552 var responseHeaders = null, 13553 response = null, 13554 statusText = ''; 13555 13556 if(status !== ABORTED) { 13557 responseHeaders = xhr.getAllResponseHeaders(); 13558 13559 // responseText is the old-school way of retrieving response (supported by IE8 & 9) 13560 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) 13561 response = ('response' in xhr) ? xhr.response : xhr.responseText; 13562 } 13563 13564 // Accessing statusText on an aborted xhr object will 13565 // throw an 'c00c023f error' in IE9 and lower, don't touch it. 13566 if (!(status === ABORTED && msie < 10)) { 13567 statusText = xhr.statusText; 13568 } 13569 13570 completeRequest(callback, 13571 status || xhr.status, 13572 response, 13573 responseHeaders, 13574 statusText); 13575 } 13576 }; 13577 13578 if (withCredentials) { 13579 xhr.withCredentials = true; 13580 } 13581 13582 if (responseType) { 13583 try { 13584 xhr.responseType = responseType; 13585 } catch (e) { 13586 // WebKit added support for the json responseType value on 09/03/2013 13587 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are 13588 // known to throw when setting the value "json" as the response type. Other older 13589 // browsers implementing the responseType 13590 // 13591 // The json response type can be ignored if not supported, because JSON payloads are 13592 // parsed on the client-side regardless. 13593 if (responseType !== 'json') { 13594 throw e; 13595 } 13596 } 13597 } 13598 13599 xhr.send(post || null); 13600 } 13601 13602 if (timeout > 0) { 13603 var timeoutId = $browserDefer(timeoutRequest, timeout); 13604 }
13604 else if (isPromiseLike(timeout)) { 13605 timeout.then(timeoutRequest); 13606 } 13607 13608 13609 function timeoutRequest() { 13610 status = ABORTED; 13611 jsonpDone && jsonpDone(); 13612 xhr && xhr.abort(); 13613 } 13614 13615 function completeRequest(callback, status, response, headersString, statusText) { 13616 // cancel timeout and subsequent timeout promise resolution 13617 timeoutId && $browserDefer.cancel(timeoutId); 13618 jsonpDone = xhr = null; 13619 13620 // fix status code when it is 0 (0 status is undocumented). 13621 // Occurs when accessing file resources or on Android 4.1 stock browser 13622 // while retrieving files from application cache. 13623 if (status === 0) { 13624 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; 13625 } 13626 13627 // normalize IE bug (http://bugs.jquery.com/ticket/1450) 13628 status = status === 1223 ? 204 : status; 13629 statusText = statusText || ''; 13630 13631 callback(status, response, headersString, statusText); 13632 $browser.$$completeOutstandingRequest(noop); 13633 } 13634 }; 13635 13636 function jsonpReq(url, callbackId, done) { 13637 // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: 13638 // - fetches local scripts via XHR and evals them 13639 // - adds and immediately removes script elements from the document 13640 var script = rawDocument.createElement('script'), callback = null; 13641 script.type = "text/javascript"; 13642 script.src = url; 13643 script.async = true; 13644 13645 callback = function(event) { 13646 removeEventListenerFn(script, "load", callback); 13647 removeEventListenerFn(script, "error", callback); 13648 rawDocument.body.removeChild(script); 13649 script = null; 13650 var status = -1; 13651 var text = "unknown"; 13652 13653 if (event) { 13654 if (event.type === "load" && !callbacks[callbackId].called) { 13655 event = { type: "error" }; 13656 } 13657 text = event.type; 13658 status = event.type === "error" ? 404 : 200; 13659 } 13660 13661 if (done) { 13662 done(status, text); 13663 } 13664 }; 13665 13666 addEventListenerFn(script, "load", callback); 13667 addEventListenerFn(script, "error", callback); 13668 13669 if (msie <= 8) { 13670 script.onreadystatechange = function() { 13671 if (isString(script.readyState) && /loaded|complete/.test(script.readyState)) { 13672 script.onreadystatechange = null; 13673 callback({ 13674 type: 'load' 13675 }); 13676 } 13677 }; 13678 } 13679 13680 rawDocument.body.appendChild(script); 13681 return callback; 13682 } 13683} 13684 13685var $interpolateMinErr = minErr('$interpolate'); 13686 13687/** 13688 * @ngdoc provider 13689 * @name $interpolateProvider 13690 * @kind function 13691 * 13692 * @description 13693 *
13694 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. 13695 * 13696 * @example 13697<example module="customInterpolationApp"> 13698<file name="index.html"> 13699<script> 13700 var customInterpolationApp = angular.module('customInterpolationApp', []); 13701 13702 customInterpolationApp.config(function($interpolateProvider) { 13703 $interpolateProvider.startSymbol('//'); 13704 $interpolateProvider.endSymbol('//'); 13705 }); 13706 13707 13708 customInterpolationApp.controller('DemoController', function() { 13709 this.label = "This binding is brought you by // interpolation symbols."; 13710 }); 13711</script> 13712<div ng-app="App" ng-controller="DemoController as demo"> 13713 //demo.label// 13714</div> 13715</file> 13716<file name="protractor.js" type="protractor"> 13717 it('should interpolate binding with custom symbols', function() { 13718 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); 13719 }); 13720</file> 13721</example> 13722 */ 13723function $InterpolateProvider() { 13724 var startSymbol = '{{'; 13725 var endSymbol = '}}'; 13726 13727 /** 13728 * @ngdoc method 13729 * @name $interpolateProvider#startSymbol 13730 * @description 13731 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. 13732 * 13733 * @param {string=} value new value to set the starting symbol to. 13734 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. 13735 */ 13736 this.startSymbol = function(value){ 13737 if (value) { 13738 startSymbol = value; 13739 return this; 13740 } else { 13741 return startSymbol; 13742 } 13743 }; 13744 13745 /** 13746 * @ngdoc method 13747 * @name $interpolateProvider#endSymbol 13748 * @description 13749 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. 13750 * 13751 * @param {string=} value new value to set the ending symbol to. 13752 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. 13753 */ 13754 this.endSymbol = function(value){ 13755 if (value) { 13756 endSymbol = value; 13757 return this; 13758 } else { 13759 return endSymbol; 13760 } 13761 }; 13762 13763 13764 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { 13765 var startSymbolLength = startSymbol.length, 13766 endSymbolLength = endSymbol.length; 13767 13768 /** 13769 * @ngdoc service 13770 * @name $interpolate 13771 * @kind function 13772 * 13773 * @requires $parse 13774 * @requires $sce 13775 * 13776 * @description 13777 * 13778 * Compiles a string with markup into an interpolation function. This service is used by the 13779 * HTML {@link ng.$compile $compile} service for data binding. See 13780 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the 13781 * interpolation markup. 13782 * 13783 * 13784 * ```js 13785 * var $interpolate = ...; // injected 13786 * var exp = $interpolate('Hello {{name | uppercase}}!'); 13787 * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!'); 13788 * ``` 13789 * 13790 * 13791 * @param {string} text The text with markup to interpolate. 13792 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have 13793 * embedded expression in order to return an interpolation function. Strings with no 13794 * embedded expression will return null for the interpolation function. 13795 * @param {string=} trustedContext when provided, the returned function passes the interpolated 13796 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, 13797 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that 13798 * provides Strict Contextual Escaping for details. 13799 * @returns {function(context)} an interpolation function which is used to compute the 13800 * interpolated string. The function has these parameters: 13801 * 13802 * * `context`: an object against which any expressions embedded in the strings are evaluated 13803 * against. 13804 * 13805 */ 13806 function $interpolate(text, mustHaveExpression, trustedContext) { 13807 var startIndex, 13808 endIndex, 13809 index = 0, 13810 parts = [], 13811 length = text.length, 13812 hasInterpolation = false, 13813 fn, 13814 exp, 13815 concat = []; 13816 13817 while(index < length) { 13818 if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && 13819 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { 13820 (index != startIndex) && parts.push(text.substring(index, startIndex)); 13821 parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); 13822 fn.exp = exp;
13823 index = endIndex + endSymbolLength; 13824 hasInterpolation = true; 13825 } else { 13826 // we did not find anything, so we have to add the remainder to the parts array 13827 (index != length) && parts.push(text.substring(index)); 13828 index = length; 13829 } 13830 } 13831 13832 if (!(length = parts.length)) { 13833 // we added, nothing, must have been an empty string. 13834 parts.push(''); 13835 length = 1; 13836 } 13837 13838 // Concatenating expressions makes it hard to reason about whether some combination of 13839 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a 13840 // single expression be used for iframe[src], object[src], etc., we ensure that the value 13841 // that's used is assigned or constructed by some JS code somewhere that is more testable or 13842 // make it obvious that you bound the value to some user controlled value. This helps reduce 13843 // the load when auditing for XSS issues. 13844 if (trustedContext && parts.length > 1) { 13845 throw $interpolateMinErr('noconcat', 13846 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + 13847 "interpolations that concatenate multiple expressions when a trusted value is " + 13848 "required. See http://docs.angularjs.org/api/ng.$sce", text); 13849 } 13850 13851 if (!mustHaveExpression || hasInterpolation) { 13852 concat.length = length; 13853 fn = function(context) { 13854 try { 13855 for(var i = 0, ii = length, part; i<ii; i++) { 13856 if (typeof (part = parts[i]) == 'function') { 13857 part = part(context); 13858 if (trustedContext) { 13859 part = $sce.getTrusted(trustedContext, part); 13860 } else { 13861 part = $sce.valueOf(part); 13862 } 13863 if (part == null) { // null || undefined 13864 part = ''; 13865 } else { 13866 switch (typeof part) { 13867 case 'string': 13868 { 13869 break; 13870 } 13871 case 'number': 13872 { 13873 part = '' + part; 13874 break; 13875 } 13876 default: 13877 { 13878 part = toJson(part); 13879 } 13880 } 13881 } 13882 } 13883 concat[i] = part; 13884 } 13885 return concat.join(''); 13886 } 13887 catch(err) { 13888 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, 13889 err.toString()); 13890 $exceptionHandler(newErr); 13891 } 13892 }; 13893 fn.exp = text; 13894 fn.parts = parts; 13895 return fn; 13896 } 13897 } 13898 13899 13900 /** 13901 * @ngdoc method 13902 * @name $interpolate#startSymbol 13903 * @description 13904 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. 13905 * 13906 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change 13907 * the symbol. 13908 * 13909 * @returns {string} start symbol. 13910 */ 13911 $interpolate.startSymbol = function() { 13912 return startSymbol; 13913 }; 13914 13915 13916 /** 13917 * @ngdoc method 13918 * @name $interpolate#endSymbol 13919 * @description 13920 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. 13921 * 13922 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change 13923 * the symbol. 13924 * 13925 * @returns {string} end symbol. 13926 */ 13927 $interpolate.endSymbol = function() { 13928 return endSymbol; 13929 }; 13930 13931 return $interpolate; 13932 }]; 13933} 13934 13935function $IntervalProvider() { 13936 this.$get = ['$rootScope', '$window', '$q', 13937 function($rootScope, $window, $q) { 13938 var intervals = {}; 13939 13940 13941 /** 13942 * @ngdoc service 13943 * @name $interval 13944 * 13945 * @description 13946 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` 13947 * milliseconds. 13948 * 13949 * The return value of registering an interval function is a promise. This promise will be 13950 * notified upon each tick of the interval, and will be resolved after `count` iterations, or 13951 * run indefinitely if `count` is not defined. The value of the notification will be the 13952 * number of iterations that have run. 13953 * To cancel an interval, call `$interval.cancel(promise)`. 13954 * 13955 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to 13956 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that 13957 * time. 13958 * 13959 * <div class="alert alert-warning">
13960 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished 13961 * with them. In particular they are not automatically destroyed when a controller's scope or a 13962 * directive's element are destroyed. 13963 * You should take this into consideration and make sure to always cancel the interval at the 13964 * appropriate moment. See the example below for more details on how and when to do this. 13965 * </div> 13966 * 13967 * @param {function()} fn A function that should be called repeatedly. 13968 * @param {number} delay Number of milliseconds between each function call. 13969 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat 13970 * indefinitely. 13971 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise 13972 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. 13973 * @returns {promise} A promise which will be notified on each iteration. 13974 * 13975 * @example 13976 * <example module="intervalExample"> 13977 * <file name="index.html"> 13978 * <script> 13979 * angular.module('intervalExample', []) 13980 * .controller('ExampleController', ['$scope', '$interval', 13981 * function($scope, $interval) { 13982 * $scope.format = 'M/d/yy h:mm:ss a'; 13983 * $scope.blood_1 = 100; 13984 * $scope.blood_2 = 120; 13985 * 13986 * var stop; 13987 * $scope.fight = function() { 13988 * // Don't start a new fight if we are already fighting 13989 * if ( angular.isDefined(stop) ) return; 13990 * 13991 * stop = $interval(function() { 13992 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { 13993 * $scope.blood_1 = $scope.blood_1 - 3; 13994 * $scope.blood_2 = $scope.blood_2 - 4; 13995 * } else { 13996 * $scope.stopFight(); 13997 * } 13998 * }, 100); 13999 * }; 14000 * 14001 * $scope.stopFight = function() { 14002 * if (angular.isDefined(stop)) { 14003 * $interval.cancel(stop); 14004 * stop = undefined; 14005 * } 14006 * }; 14007 * 14008 * $scope.resetFight = function() { 14009 * $scope.blood_1 = 100; 14010 * $scope.blood_2 = 120; 14011 * }; 14012 * 14013 * $scope.$on('$destroy', function() { 14014 * // Make sure that the interval is destroyed too 14015 * $scope.stopFight(); 14016 * }); 14017 * }]) 14018 * // Register the 'myCurrentTime' directive factory method. 14019 * // We inject $interval and dateFilter service since the factory method is DI. 14020 * .directive('myCurrentTime', ['$interval', 'dateFilter', 14021 * function($interval, dateFilter) { 14022 * // return the directive link function. (compile function not needed) 14023 * return function(scope, element, attrs) { 14024 * var format, // date format 14025 * stopTime; // so that we can cancel the time updates 14026 * 14027 * // used to update the UI 14028 * function updateTime() { 14029 * element.text(dateFilter(new Date(), format)); 14030 * } 14031 * 14032 * // watch the expression, and update the UI on change. 14033 * scope.$watch(attrs.myCurrentTime, function(value) { 14034 * format = value; 14035 * updateTime(); 14036 * }); 14037 * 14038 * stopTime = $interval(updateTime, 1000); 14039 * 14040 * // listen on DOM destroy (removal) event, and cancel the next UI update 14041 * // to prevent updating time after the DOM element was removed. 14042 * element.bind('$destroy', function() { 14043 * $interval.cancel(stopTime); 14044 * }); 14045 * } 14046 * }]); 14047 * </script> 14048 * 14049 * <div>
14050 * <div ng-controller="ExampleController"> 14051 * Date format: <input ng-model="format"> <hr/> 14052 * Current time is: <span my-current-time="format"></span> 14053 * <hr/> 14054 * Blood 1 : <font color='red'>{{blood_1}}</font> 14055 * Blood 2 : <font color='red'>{{blood_2}}</font> 14056 * <button type="button" data-ng-click="fight()">Fight</button> 14057 * <button type="button" data-ng-click="stopFight()">StopFight</button> 14058 * <button type="button" data-ng-click="resetFight()">resetFight</button> 14059 * </div> 14060 * </div> 14061 * 14062 * </file> 14063 * </example> 14064 */ 14065 function interval(fn, delay, count, invokeApply) { 14066 var setInterval = $window.setInterval, 14067 clearInterval = $window.clearInterval, 14068 deferred = $q.defer(), 14069 promise = deferred.promise, 14070 iteration = 0, 14071 skipApply = (isDefined(invokeApply) && !invokeApply); 14072 14073 count = isDefined(count) ? count : 0; 14074 14075 promise.then(null, null, fn); 14076 14077 promise.$$intervalId = setInterval(function tick() { 14078 deferred.notify(iteration++); 14079 14080 if (count > 0 && iteration >= count) { 14081 deferred.resolve(iteration); 14082 clearInterval(promise.$$intervalId); 14083 delete intervals[promise.$$intervalId]; 14084 } 14085 14086 if (!skipApply) $rootScope.$apply(); 14087 14088 }, delay); 14089 14090 intervals[promise.$$intervalId] = deferred; 14091 14092 return promise; 14093 } 14094 14095 14096 /** 14097 * @ngdoc method 14098 * @name $interval#cancel 14099 * 14100 * @description 14101 * Cancels a task associated with the `promise`. 14102 * 14103 * @param {promise} promise returned by the `$interval` function. 14104 * @returns {boolean} Returns `true` if the task was successfully canceled. 14105 */ 14106 interval.cancel = function(promise) { 14107 if (promise && promise.$$intervalId in intervals) { 14108 intervals[promise.$$intervalId].reject('canceled'); 14109 $window.clearInterval(promise.$$intervalId); 14110 delete intervals[promise.$$intervalId]; 14111 return true; 14112 } 14113 return false; 14114 }; 14115 14116 return interval; 14117 }]; 14118} 14119 14120/** 14121 * @ngdoc service 14122 * @name $locale 14123 * 14124 * @description 14125 * $locale service provides localization rules for various Angular components. As of right now the 14126 * only public api is: 14127 * 14128 * * `id` â `{string}` â locale id formatted as `languageId-countryId` (e.g. `en-us`) 14129 */ 14130function $LocaleProvider(){ 14131 this.$get = function() { 14132 return { 14133 id: 'en-us', 14134 14135 NUMBER_FORMATS: { 14136 DECIMAL_SEP: '.', 14137 GROUP_SEP: ',', 14138 PATTERNS: [ 14139 { // Decimal Pattern 14140 minInt: 1, 14141 minFrac: 0, 14142 maxFrac: 3, 14143 posPre: '', 14144 posSuf: '', 14145 negPre: '-', 14146 negSuf: '', 14147 gSize: 3, 14148 lgSize: 3 14149 },{ //Currency Pattern 14150 minInt: 1, 14151 minFrac: 2, 14152 maxFrac: 2, 14153 posPre: '\u00A4', 14154 posSuf: '', 14155 negPre: '(\u00A4', 14156 negSuf: ')', 14157 gSize: 3, 14158 lgSize: 3 14159 } 14160 ], 14161 CURRENCY_SYM: '$' 14162 }, 14163 14164 DATETIME_FORMATS: { 14165 MONTH: 14166 'January,February,March,April,May,June,July,August,September,October,November,December' 14167 .split(','), 14168 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), 14169 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), 14170 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), 14171 AMPMS: ['AM','PM'], 14172 medium: 'MMM d, y h:mm:ss a', 14173 short: 'M/d/yy h:mm a', 14174 fullDate: 'EEEE, MMMM d, y', 14175 longDate: 'MMMM d, y', 14176 mediumDate: 'MMM d, y', 14177 shortDate: 'M/d/yy', 14178 mediumTime: 'h:mm:ss a', 14179 shortTime: 'h:mm a' 14180 }, 14181 14182 pluralCat: function(num) { 14183 if (num === 1) { 14184 return 'one'; 14185 } 14186 return 'other'; 14187 } 14188 }; 14189 }; 14190} 14191 14192var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, 14193 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; 14194var $locationMinErr = minErr('$location'); 14195 14196 14197/** 14198 * Encode path using encodeUriSegment, ignoring forward slashes 14199 * 14200 * @param {string} path Path to encode 14201 * @returns {string} 14202 */ 14203function encodePath(path) { 14204 var segments = path.split('/'), 14205 i = segments.length; 14206 14207 while (i--) { 14208 segments[i] = encodeUriSegment(segments[i]); 14209 } 14210 14211 return segments.join('/'); 14212} 14213 14214function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
14215 var parsedUrl = urlResolve(absoluteUrl, appBase); 14216 14217 locationObj.$$protocol = parsedUrl.protocol; 14218 locationObj.$$host = parsedUrl.hostname; 14219 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; 14220} 14221 14222 14223function parseAppUrl(relativeUrl, locationObj, appBase) { 14224 var prefixed = (relativeUrl.charAt(0) !== '/'); 14225 if (prefixed) { 14226 relativeUrl = '/' + relativeUrl; 14227 } 14228 var match = urlResolve(relativeUrl, appBase); 14229 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? 14230 match.pathname.substring(1) : match.pathname); 14231 locationObj.$$search = parseKeyValue(match.search); 14232 locationObj.$$hash = decodeURIComponent(match.hash); 14233 14234 // make sure path starts with '/'; 14235 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { 14236 locationObj.$$path = '/' + locationObj.$$path; 14237 } 14238} 14239 14240 14241/** 14242 * 14243 * @param {string} begin 14244 * @param {string} whole 14245 * @returns {string} returns text from whole after begin or undefined if it does not begin with 14246 * expected string. 14247 */ 14248function beginsWith(begin, whole) { 14249 if (whole.indexOf(begin) === 0) { 14250 return whole.substr(begin.length); 14251 } 14252} 14253 14254 14255function stripHash(url) { 14256 var index = url.indexOf('#'); 14257 return index == -1 ? url : url.substr(0, index); 14258} 14259 14260 14261function stripFile(url) { 14262 return url.substr(0, stripHash(url).lastIndexOf('/') + 1); 14263} 14264 14265/* return the server only (scheme://host:port) */ 14266function serverBase(url) { 14267 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); 14268} 14269 14270 14271/** 14272 * LocationHtml5Url represents an url 14273 * This object is exposed as $location service when HTML5 mode is enabled and supported 14274 * 14275 * @constructor 14276 * @param {string} appBase application base URL 14277 * @param {string} basePrefix url path prefix 14278 */ 14279function LocationHtml5Url(appBase, basePrefix) { 14280 this.$$html5 = true; 14281 basePrefix = basePrefix || ''; 14282 var appBaseNoFile = stripFile(appBase); 14283 parseAbsoluteUrl(appBase, this, appBase); 14284 14285 14286 /** 14287 * Parse given html5 (regular) url string into properties 14288 * @param {string} newAbsoluteUrl HTML5 url 14289 * @private 14290 */ 14291 this.$$parse = function(url) { 14292 var pathUrl = beginsWith(appBaseNoFile, url); 14293 if (!isString(pathUrl)) { 14294 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, 14295 appBaseNoFile); 14296 } 14297 14298 parseAppUrl(pathUrl, this, appBase); 14299 14300 if (!this.$$path) { 14301 this.$$path = '/'; 14302 } 14303 14304 this.$$compose(); 14305 }; 14306 14307 /** 14308 * Compose url and update `absUrl` property 14309 * @private 14310 */ 14311 this.$$compose = function() { 14312 var search = toKeyValue(this.$$search), 14313 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 14314 14315 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 14316 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' 14317 }; 14318 14319 this.$$rewrite = function(url) { 14320 var appUrl, prevAppUrl; 14321 14322 if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { 14323 prevAppUrl = appUrl; 14324 if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { 14325 return appBaseNoFile + (beginsWith('/', appUrl) || appUrl); 14326 } else { 14327 return appBase + prevAppUrl; 14328 } 14329 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { 14330 return appBaseNoFile + appUrl; 14331 } else if (appBaseNoFile == url + '/') { 14332 return appBaseNoFile; 14333 } 14334 }; 14335} 14336 14337 14338/** 14339 * LocationHashbangUrl represents url 14340 * This object is exposed as $location service when developer doesn't opt into html5 mode. 14341 * It also serves as the base class for html5 mode fallback on legacy browsers. 14342 * 14343 * @constructor 14344 * @param {string} appBase application base URL 14345 * @param {string} hashPrefix hashbang prefix 14346 */ 14347function LocationHashbangUrl(appBase, hashPrefix) { 14348 var appBaseNoFile = stripFile(appBase); 14349 14350 parseAbsoluteUrl(appBase, this, appBase); 14351 14352 14353 /** 14354 * Parse given hashbang url into properties 14355 * @param {string} url Hashbang url 14356 * @private 14357 */ 14358 this.$$parse = function(url) { 14359 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); 14360 var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' 14361 ? beginsWith(hashPrefix, withoutBaseUrl) 14362 : (this.$$html5) 14363 ? withoutBaseUrl 14364 : ''; 14365 14366 if (!isString(withoutHashUrl)) { 14367 throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, 14368 hashPrefix); 14369 } 14370 parseAppUrl(withoutHashUrl, this, appBase); 14371 14372 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); 14373 14374 this.$$compose(); 14375 14376 /* 14377 * In Windows, on an anchor node on documents loaded from 14378 * the filesystem, the browser will return a pathname
14379 * prefixed with the drive name ('/C:/path') when a 14380 * pathname without a drive is set: 14381 * * a.setAttribute('href', '/foo') 14382 * * a.pathname === '/C:/foo' //true 14383 * 14384 * Inside of Angular, we're always using pathnames that 14385 * do not include drive names for routing. 14386 */ 14387 function removeWindowsDriveName (path, url, base) { 14388 /* 14389 Matches paths for file protocol on windows, 14390 such as /C:/foo/bar, and captures only /foo/bar. 14391 */ 14392 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; 14393 14394 var firstPathSegmentMatch; 14395 14396 //Get the relative path from the input URL. 14397 if (url.indexOf(base) === 0) { 14398 url = url.replace(base, ''); 14399 } 14400 14401 // The input URL intentionally contains a first path segment that ends with a colon. 14402 if (windowsFilePathExp.exec(url)) { 14403 return path; 14404 } 14405 14406 firstPathSegmentMatch = windowsFilePathExp.exec(path); 14407 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; 14408 } 14409 }; 14410 14411 /** 14412 * Compose hashbang url and update `absUrl` property 14413 * @private 14414 */ 14415 this.$$compose = function() { 14416 var search = toKeyValue(this.$$search), 14417 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 14418 14419 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 14420 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); 14421 }; 14422 14423 this.$$rewrite = function(url) { 14424 if(stripHash(appBase) == stripHash(url)) { 14425 return url; 14426 } 14427 }; 14428} 14429 14430 14431/** 14432 * LocationHashbangUrl represents url 14433 * This object is exposed as $location service when html5 history api is enabled but the browser 14434 * does not support it. 14435 * 14436 * @constructor 14437 * @param {string} appBase application base URL 14438 * @param {string} hashPrefix hashbang prefix 14439 */ 14440function LocationHashbangInHtml5Url(appBase, hashPrefix) { 14441 this.$$html5 = true; 14442 LocationHashbangUrl.apply(this, arguments); 14443 14444 var appBaseNoFile = stripFile(appBase); 14445 14446 this.$$rewrite = function(url) { 14447 var appUrl; 14448 14449 if ( appBase == stripHash(url) ) { 14450 return url; 14451 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { 14452 return appBase + hashPrefix + appUrl; 14453 } else if ( appBaseNoFile === url + '/') { 14454 return appBaseNoFile; 14455 } 14456 }; 14457 14458 this.$$compose = function() { 14459 var search = toKeyValue(this.$$search), 14460 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; 14461 14462 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; 14463 // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#' 14464 this.$$absUrl = appBase + hashPrefix + this.$$url; 14465 }; 14466 14467} 14468 14469 14470LocationHashbangInHtml5Url.prototype = 14471 LocationHashbangUrl.prototype = 14472 LocationHtml5Url.prototype = { 14473 14474 /** 14475 * Are we in html5 mode? 14476 * @private 14477 */ 14478 $$html5: false, 14479 14480 /** 14481 * Has any change been replacing ? 14482 * @private 14483 */ 14484 $$replace: false, 14485 14486 /** 14487 * @ngdoc method 14488 * @name $location#absUrl 14489 * 14490 * @description 14491 * This method is getter only. 14492 * 14493 * Return full url representation with all segments encoded according to rules specified in 14494 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). 14495 * 14496 * @return {string} full url 14497 */ 14498 absUrl: locationGetter('$$absUrl'), 14499 14500 /** 14501 * @ngdoc method 14502 * @name $location#url 14503 * 14504 * @description 14505 * This method is getter / setter. 14506 * 14507 * Return url (e.g. `/path?a=b#hash`) when called without any parameter. 14508 * 14509 * Change path, search and hash, when called with parameter and return `$location`. 14510 * 14511 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) 14512 * @return {string} url 14513 */ 14514 url: function(url) { 14515 if (isUndefined(url)) 14516 return this.$$url; 14517 14518 var match = PATH_MATCH.exec(url); 14519 if (match[1]) this.path(decodeURIComponent(match[1])); 14520 if (match[2] || match[1]) this.search(match[3] || ''); 14521 this.hash(match[5] || ''); 14522 14523 return this; 14524 }, 14525 14526 /** 14527 * @ngdoc method 14528 * @name $location#protocol 14529 * 14530 * @description 14531 * This method is getter only. 14532 * 14533 * Return protocol of current url. 14534 * 14535 * @return {string} protocol of current url 14536 */ 14537 protocol: locationGetter('$$protocol'), 14538 14539 /** 14540 * @ngdoc method 14541 * @name $location#host 14542 * 14543 * @description 14544 * This method is getter only. 14545 * 14546 * Return host of current url. 14547 * 14548 * @return {string} host of current url. 14549 */ 14550 host: locationGetter('$$host'), 14551 14552 /** 14553 * @ngdoc method 14554 * @name $location#port 14555 * 14556 * @description 14557 * This method is getter only. 14558 * 14559 * Return port of current url. 14560 * 14561 * @return {Number} port 14562 */ 14563 port: locationGetter('$$port'), 14564 14565 /** 14566 * @ngdoc method 14567 * @name $location#path 14568 * 14569 * @description 14570 * This method is getter / setter. 14571 * 14572 * Return path of current url when called without any parameter. 14573 * 14574 * Change path when called with parameter and return `$location`. 14575 * 14576 * Note: Path should always begin with forward slash (/), this method will add the forward slash 14577 * if it is missing. 14578 * 14579 * @param {(string|number)=} path New path 14580 * @return {string} path 14581 */ 14582 path: locationGetterSetter('$$path', function(path) { 14583 path = path ? path.toString() : ''; 14584 return path.charAt(0) == '/' ? path : '/' + path; 14585 }), 14586 14587 /** 14588 * @ngdoc method 14589 * @name $location#search 14590 * 14591 * @description 14592 * This method is getter / setter. 14593 * 14594 * Return search part (as object) of current url when called without any parameter. 14595 * 14596 * Change search part when called with parameter and return `$location`. 14597 * 14598 * 14599 * ```js
14600 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo 14601 * var searchObject = $location.search(); 14602 * // => {foo: 'bar', baz: 'xoxo'} 14603 * 14604 * 14605 * // set foo to 'yipee' 14606 * $location.search('foo', 'yipee'); 14607 * // => $location 14608 * ``` 14609 * 14610 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or 14611 * hash object. 14612 * 14613 * When called with a single argument the method acts as a setter, setting the `search` component 14614 * of `$location` to the specified value. 14615 * 14616 * If the argument is a hash object containing an array of values, these values will be encoded 14617 * as duplicate search parameters in the url. 14618 * 14619 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` 14620 * will override only a single search property. 14621 * 14622 * If `paramValue` is an array, it will override the property of the `search` component of 14623 * `$location` specified via the first argument. 14624 * 14625 * If `paramValue` is `null`, the property specified via the first argument will be deleted. 14626 * 14627 * If `paramValue` is `true`, the property specified via the first argument will be added with no 14628 * value nor trailing equal sign. 14629 * 14630 * @return {Object} If called with no arguments returns the parsed `search` object. If called with 14631 * one or more arguments returns `$location` object itself. 14632 */ 14633 search: function(search, paramValue) { 14634 switch (arguments.length) { 14635 case 0: 14636 return this.$$search; 14637 case 1: 14638 if (isString(search) || isNumber(search)) { 14639 search = search.toString(); 14640 this.$$search = parseKeyValue(search); 14641 } else if (isObject(search)) { 14642 // remove object undefined or null properties 14643 forEach(search, function(value, key) { 14644 if (value == null) delete search[key]; 14645 }); 14646 14647 this.$$search = search; 14648 } else { 14649 throw $locationMinErr('isrcharg', 14650 'The first argument of the `$location#search()` call must be a string or an object.'); 14651 } 14652 break; 14653 default: 14654 if (isUndefined(paramValue) || paramValue === null) { 14655 delete this.$$search[search]; 14656 } else { 14657 this.$$search[search] = paramValue; 14658 } 14659 } 14660 14661 this.$$compose(); 14662 return this; 14663 }, 14664 14665 /** 14666 * @ngdoc method 14667 * @name $location#hash 14668 * 14669 * @description 14670 * This method is getter / setter. 14671 * 14672 * Return hash fragment when called without any parameter. 14673 * 14674 * Change hash fragment when called with parameter and return `$location`. 14675 * 14676 * @param {(string|number)=} hash New hash fragment 14677 * @return {string} hash 14678 */ 14679 hash: locationGetterSetter('$$hash', function(hash) { 14680 return hash ? hash.toString() : ''; 14681 }), 14682 14683 /** 14684 * @ngdoc method 14685 * @name $location#replace 14686 * 14687 * @description 14688 * If called, all changes to $location during current `$digest` will be replacing current history 14689 * record, instead of adding new one. 14690 */ 14691 replace: function() { 14692 this.$$replace = true; 14693 return this; 14694 } 14695}; 14696 14697function locationGetter(property) { 14698 return function() { 14699 return this[property]; 14700 }; 14701} 14702 14703 14704function locationGetterSetter(property, preprocess) { 14705 return function(value) { 14706 if (isUndefined(value)) 14707 return this[property]; 14708 14709 this[property] = preprocess(value); 14710 this.$$compose(); 14711 14712 return this; 14713 }; 14714} 14715 14716 14717/** 14718 * @ngdoc service 14719 * @name $location 14720 * 14721 * @requires $rootElement 14722 * 14723 * @description 14724 * The $location service parses the URL in the browser address bar (based on the 14725 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL 14726 * available to your application. Changes to the URL in the address bar are reflected into 14727 * $location service and changes to $location are reflected into the browser address bar. 14728 * 14729 * **The $location service:** 14730 * 14731 * - Exposes the current URL in the browser address bar, so you can 14732 * - Watch and observe the URL. 14733 * - Change the URL. 14734 * - Synchronizes the URL with the browser when the user 14735 * - Changes the address bar. 14736 * - Clicks the back or forward button (or clicks a History link). 14737 * - Clicks on a link. 14738 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). 14739 * 14740 * For more information see {@link guide/$location Developer Guide: Using $location} 14741 */ 14742 14743/** 14744 * @ngdoc provider 14745 * @name $locationProvider 14746 * @description 14747 * Use the `$locationProvider` to configure how the application deep linking paths are stored. 14748 */ 14749function $LocationProvider(){ 14750 var hashPrefix = '', 14751 html5Mode = false; 14752 14753 /** 14754 * @ngdoc method 14755 * @name $locationProvider#hashPrefix 14756 * @description 14757 * @param {string=} prefix Prefix for hash part (containing path and search) 14758 * @returns {*} current value if used as getter or itself (chaining) if used as setter 14759 */ 14760 this.hashPrefix = function(prefix) { 14761 if (isDefined(prefix)) { 14762 hashPrefix = prefix; 14763 return this; 14764 } else { 14765 return hashPrefix; 14766 } 14767 }; 14768 14769 /** 14770 * @ngdoc method 14771 * @name $locationProvider#html5Mode 14772 * @description 14773 * @param {boolean=} mode Use HTML5 strategy if available. 14774 * @returns {*} current value if used as getter or itself (chaining) if used as setter 14775 */ 14776 this.html5Mode = function(mode) { 14777 if (isDefined(mode)) { 14778 html5Mode = mode; 14779 return this; 14780 } else { 14781 return html5Mode; 14782 } 14783 }; 14784 14785 /** 14786 * @ngdoc event 14787 * @name $location#$locationChangeStart 14788 * @eventType broadcast on root scope 14789 * @description 14790 * Broadcasted before a URL will change. This change can be prevented by calling 14791 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more 14792 * details about event object. Upon successful change 14793 * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired. 14794 * 14795 * @param {Object} angularEvent Synthetic event object. 14796 * @param {string} newUrl New URL 14797 * @param {string=} oldUrl URL that was before it was changed. 14798 */ 14799 14800 /** 14801 * @ngdoc event 14802 * @name $location#$locationChangeSuccess 14803 * @eventType broadcast on root scope 14804 * @description 14805 * Broadcasted after a URL was changed. 14806 * 14807 * @param {Object} angularEvent Synthetic event object. 14808 * @param {string} newUrl New URL 14809 * @param {string=} oldUrl URL that was before it was changed. 14810 */ 14811 14812 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', 14813 function( $rootScope, $browser, $sniffer, $rootElement) { 14814 var $location, 14815 LocationMode, 14816 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' 14817 initialUrl = $browser.url(), 14818 appBase; 14819 14820 if (html5Mode) { 14821 appBase = serverBase(initialUrl) + (baseHref || '/'); 14822 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; 14823 } else { 14824 appBase = stripHash(initialUrl); 14825 LocationMode = LocationHashbangUrl; 14826 }
14827 $location = new LocationMode(appBase, '#' + hashPrefix); 14828 $location.$$parse($location.$$rewrite(initialUrl)); 14829 14830 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; 14831 14832 $rootElement.on('click', function(event) { 14833 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) 14834 // currently we open nice url link and redirect then 14835 14836 if (event.ctrlKey || event.metaKey || event.which == 2) return; 14837 14838 var elm = jqLite(event.target); 14839 14840 // traverse the DOM up to find first A tag 14841 while (lowercase(elm[0].nodeName) !== 'a') { 14842 // ignore rewriting if no A tag (reached root element, or no parent - removed from document) 14843 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; 14844 } 14845 14846 var absHref = elm.prop('href'); 14847 14848 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { 14849 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during 14850 // an animation. 14851 absHref = urlResolve(absHref.animVal).href; 14852 } 14853 14854 // Ignore when url is started with javascript: or mailto: 14855 if (IGNORE_URI_REGEXP.test(absHref)) return; 14856 14857 // Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9) 14858 // The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or 14859 // somewhere#anchor or http://example.com/somewhere 14860 if (LocationMode === LocationHashbangInHtml5Url) { 14861 // get the actual href attribute - see 14862 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx 14863 var href = elm.attr('href') || elm.attr('xlink:href'); 14864 14865 if (href && href.indexOf('://') < 0) { // Ignore absolute URLs 14866 var prefix = '#' + hashPrefix; 14867 if (href[0] == '/') { 14868 // absolute path - replace old path 14869 absHref = appBase + prefix + href; 14870 } else if (href[0] == '#') { 14871 // local anchor 14872 absHref = appBase + prefix + ($location.path() || '/') + href; 14873 } else { 14874 // relative path - join with current path 14875 var stack = $location.path().split("/"), 14876 parts = href.split("/"); 14877 if (stack.length === 2 && !stack[1]) stack.length = 1; 14878 for (var i=0; i<parts.length; i++) { 14879 if (parts[i] == ".") 14880 continue; 14881 else if (parts[i] == "..") 14882 stack.pop(); 14883 else if (parts[i].length) 14884 stack.push(parts[i]); 14885 } 14886 absHref = appBase + prefix + stack.join('/'); 14887 } 14888 } 14889 } 14890 14891 var rewrittenUrl = $location.$$rewrite(absHref); 14892 14893 if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) { 14894 event.preventDefault(); 14895 if (rewrittenUrl != $browser.url()) { 14896 // update location manually 14897 $location.$$parse(rewrittenUrl); 14898 $rootScope.$apply(); 14899 // hack to work around FF6 bug 684208 when scenario runner clicks on links 14900 window.angular['ff-684208-preventDefault'] = true; 14901 } 14902 } 14903 }); 14904 14905 14906 // rewrite hashbang url <> html5 url 14907 if ($location.absUrl() != initialUrl) { 14908 $browser.url($location.absUrl(), true); 14909 } 14910 14911 // update $location when $browser url changes 14912 $browser.onUrlChange(function(newUrl) { 14913 if ($location.absUrl() != newUrl) { 14914 $rootScope.$evalAsync(function() { 14915 var oldUrl = $location.absUrl(); 14916 14917 $location.$$parse(newUrl); 14918 if ($rootScope.$broadcast('$locationChangeStart', newUrl, 14919 oldUrl).defaultPrevented) { 14920 $location.$$parse(oldUrl); 14921 $browser.url(oldUrl); 14922 } else { 14923 afterLocationChange(oldUrl); 14924 } 14925 }); 14926 if (!$rootScope.$$phase) $rootScope.$digest(); 14927 } 14928 }); 14929 14930 // update browser 14931 var changeCounter = 0; 14932 $rootScope.$watch(function $locationWatch() { 14933 var oldUrl = $browser.url(); 14934 var currentReplace = $location.$$replace; 14935 14936 if (!changeCounter || oldUrl != $location.absUrl()) { 14937 changeCounter++; 14938 $rootScope.$evalAsync(function() { 14939 if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). 14940 defaultPrevented) { 14941 $location.$$parse(oldUrl); 14942 } else { 14943 $browser.url($location.absUrl(), currentReplace); 14944 afterLocationChange(oldUrl); 14945 } 14946 }); 14947 } 14948 $location.$$replace = false; 14949 14950 return changeCounter; 14951 });
14952 14953 return $location; 14954 14955 function afterLocationChange(oldUrl) { 14956 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); 14957 } 14958}]; 14959} 14960 14961/** 14962 * @ngdoc service 14963 * @name $log 14964 * @requires $window 14965 * 14966 * @description 14967 * Simple service for logging. Default implementation safely writes the message 14968 * into the browser's console (if present). 14969 * 14970 * The main purpose of this service is to simplify debugging and troubleshooting. 14971 * 14972 * The default is to log `debug` messages. You can use 14973 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. 14974 * 14975 * @example 14976 <example module="logExample"> 14977 <file name="script.js"> 14978 angular.module('logExample', []) 14979 .controller('LogController', ['$scope', '$log', function($scope, $log) { 14980 $scope.$log = $log; 14981 $scope.message = 'Hello World!'; 14982 }]); 14983 </file> 14984 <file name="index.html"> 14985 <div ng-controller="LogController"> 14986 <p>Reload this page with open console, enter text and hit the log button...</p> 14987 Message: 14988 <input type="text" ng-model="message"/> 14989 <button ng-click="$log.log(message)">log</button> 14990 <button ng-click="$log.warn(message)">warn</button> 14991 <button ng-click="$log.info(message)">info</button> 14992 <button ng-click="$log.error(message)">error</button> 14993 </div> 14994 </file> 14995 </example> 14996 */ 14997 14998/** 14999 * @ngdoc provider 15000 * @name $logProvider 15001 * @description 15002 * Use the `$logProvider` to configure how the application logs messages 15003 */ 15004function $LogProvider(){ 15005 var debug = true, 15006 self = this; 15007 15008 /** 15009 * @ngdoc method 15010 * @name $logProvider#debugEnabled 15011 * @description 15012 * @param {boolean=} flag enable or disable debug level messages 15013 * @returns {*} current value if used as getter or itself (chaining) if used as setter 15014 */ 15015 this.debugEnabled = function(flag) { 15016 if (isDefined(flag)) { 15017 debug = flag; 15018 return this; 15019 } else { 15020 return debug; 15021 } 15022 }; 15023 15024 this.$get = ['$window', function($window){ 15025 return { 15026 /** 15027 * @ngdoc method 15028 * @name $log#log 15029 * 15030 * @description 15031 * Write a log message 15032 */ 15033 log: consoleLog('log'), 15034 15035 /** 15036 * @ngdoc method 15037 * @name $log#info 15038 * 15039 * @description 15040 * Write an information message 15041 */ 15042 info: consoleLog('info'), 15043 15044 /** 15045 * @ngdoc method 15046 * @name $log#warn 15047 * 15048 * @description 15049 * Write a warning message 15050 */ 15051 warn: consoleLog('warn'), 15052 15053 /** 15054 * @ngdoc method 15055 * @name $log#error 15056 * 15057 * @description 15058 * Write an error message 15059 */ 15060 error: consoleLog('error'), 15061 15062 /** 15063 * @ngdoc method 15064 * @name $log#debug 15065 * 15066 * @description 15067 * Write a debug message 15068 */ 15069 debug: (function () { 15070 var fn = consoleLog('debug'); 15071 15072 return function() { 15073 if (debug) { 15074 fn.apply(self, arguments); 15075 } 15076 }; 15077 }()) 15078 }; 15079 15080 function formatError(arg) { 15081 if (arg instanceof Error) { 15082 if (arg.stack) { 15083 arg = (arg.message && arg.stack.indexOf(arg.message) === -1) 15084 ? 'Error: ' + arg.message + '\n' + arg.stack 15085 : arg.stack; 15086 } else if (arg.sourceURL) { 15087 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; 15088 } 15089 } 15090 return arg; 15091 } 15092 15093 function consoleLog(type) { 15094 var console = $window.console || {}, 15095 logFn = console[type] || console.log || noop, 15096 hasApply = false; 15097 15098 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. 15099 // The reason behind this is that console.log has type "object" in IE8... 15100 try { 15101 hasApply = !!logFn.apply; 15102 } catch (e) {} 15103 15104 if (hasApply) { 15105 return function() { 15106 var args = []; 15107 forEach(arguments, function(arg) { 15108 args.push(formatError(arg)); 15109 }); 15110 return logFn.apply(console, args); 15111 }; 15112 } 15113 15114 // we are IE which either doesn't have window.console => this is noop and we do nothing, 15115 // or we are IE where console.log doesn't have apply so we log at least first 2 args 15116 return function(arg1, arg2) { 15117 logFn(arg1, arg2 == null ? '' : arg2); 15118 }; 15119 } 15120 }]; 15121} 15122 15123var $parseMinErr = minErr('$parse'); 15124var promiseWarningCache = {}; 15125var promiseWarning; 15126 15127// Sandboxing Angular Expressions 15128// ------------------------------ 15129// Angular expressions are generally considered safe because these expressions only have direct 15130// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by 15131// obtaining a reference to native JS functions such as the Function constructor. 15132// 15133// As an example, consider the following Angular expression: 15134// 15135// {}.toString.constructor('alert("evil JS code")') 15136// 15137// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits 15138// against the expression language, but not to prevent exploits that were enabled by exposing 15139// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good 15140// practice and therefore we are not even trying to protect against interaction with an object 15141// explicitly exposed in this way. 15142// 15143// In general, it is not possible to access a Window object from an angular expression unless a 15144// window or some DOM object that has a reference to window is published onto a Scope. 15145// Similarly we prevent invocations of function known to be dangerous, as well as assignments to 15146// native objects. 15147 15148 15149function ensureSafeMemberName(name, fullExpression) { 15150 if (name === "__defineGetter__" || name === "__defineSetter__" 15151 || name === "__lookupGetter__" || name === "__lookupSetter__" 15152 || name === "__proto__") { 15153 throw $parseMinErr('isecfld', 15154 'Attempting to access a disallowed field in Angular expressions! ' 15155 +'Expression: {0}', fullExpression); 15156 } 15157 return name; 15158} 15159 15160function ensureSafeObject(obj, fullExpression) { 15161 // nifty check if obj is Function that is fast and works across iframes and other contexts 15162 if (obj) { 15163 if (obj.constructor === obj) { 15164 throw $parseMinErr('isecfn', 15165 'Referencing Function in Angular expressions is disallowed! Expression: {0}', 15166 fullExpression); 15167 } else if (// isWindow(obj) 15168 obj.document && obj.location && obj.alert && obj.setInterval) { 15169 throw $parseMinErr('isecwindow', 15170 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', 15171 fullExpression); 15172 } else if (// isElement(obj) 15173 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { 15174 throw $parseMinErr('isecdom', 15175 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', 15176 fullExpression); 15177 } else if (// block Object so that we can't get hold of dangerous Object.* methods 15178 obj === Object) { 15179 throw $parseMinErr('isecobj', 15180 'Referencing Object in Angular expressions is disallowed! Expression: {0}', 15181 fullExpression); 15182 } 15183 } 15184 return obj;
15185} 15186 15187var CALL = Function.prototype.call; 15188var APPLY = Function.prototype.apply; 15189var BIND = Function.prototype.bind; 15190 15191function ensureSafeFunction(obj, fullExpression) { 15192 if (obj) { 15193 if (obj.constructor === obj) { 15194 throw $parseMinErr('isecfn', 15195 'Referencing Function in Angular expressions is disallowed! Expression: {0}', 15196 fullExpression); 15197 } else if (obj === CALL || obj === APPLY || (BIND && obj === BIND)) { 15198 throw $parseMinErr('isecff', 15199 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', 15200 fullExpression); 15201 } 15202 } 15203} 15204 15205var OPERATORS = { 15206 /* jshint bitwise : false */ 15207 'null':function(){return null;}, 15208 'true':function(){return true;}, 15209 'false':function(){return false;}, 15210 undefined:noop, 15211 '+':function(self, locals, a,b){ 15212 a=a(self, locals); b=b(self, locals); 15213 if (isDefined(a)) { 15214 if (isDefined(b)) { 15215 return a + b; 15216 } 15217 return a; 15218 } 15219 return isDefined(b)?b:undefined;}, 15220 '-':function(self, locals, a,b){ 15221 a=a(self, locals); b=b(self, locals); 15222 return (isDefined(a)?a:0)-(isDefined(b)?b:0); 15223 }, 15224 '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, 15225 '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, 15226 '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, 15227 '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, 15228 '=':noop, 15229 '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, 15230 '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, 15231 '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, 15232 '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, 15233 '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);}, 15234 '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, 15235 '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, 15236 '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, 15237 '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, 15238 '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, 15239 '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, 15240// '|':function(self, locals, a,b){return a|b;}, 15241 '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, 15242 '!':function(self, locals, a){return !a(self, locals);} 15243}; 15244/* jshint bitwise: true */ 15245var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; 15246 15247 15248///////////////////////////////////////// 15249 15250 15251/** 15252 * @constructor 15253 */ 15254var Lexer = function (options) { 15255 this.options = options; 15256}; 15257 15258Lexer.prototype = { 15259 constructor: Lexer, 15260 15261 lex: function (text) { 15262 this.text = text; 15263 15264 this.index = 0; 15265 this.ch = undefined; 15266 this.lastCh = ':'; // can start regexp 15267 15268 this.tokens = []; 15269 15270 while (this.index < this.text.length) { 15271 this.ch = this.text.charAt(this.index); 15272 if (this.is('"\'')) { 15273 this.readString(this.ch); 15274 } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { 15275 this.readNumber(); 15276 } else if (this.isIdent(this.ch)) { 15277 this.readIdent(); 15278 } else if (this.is('(){}[].,;:?')) { 15279 this.tokens.push({ 15280 index: this.index, 15281 text: this.ch 15282 }); 15283 this.index++; 15284 } else if (this.isWhitespace(this.ch)) { 15285 this.index++; 15286 continue; 15287 } else { 15288 var ch2 = this.ch + this.peek(); 15289 var ch3 = ch2 + this.peek(2); 15290 var fn = OPERATORS[this.ch]; 15291 var fn2 = OPERATORS[ch2]; 15292 var fn3 = OPERATORS[ch3]; 15293 if (fn3) { 15294 this.tokens.push({index: this.index, text: ch3, fn: fn3}); 15295 this.index += 3; 15296 } else if (fn2) { 15297 this.tokens.push({index: this.index, text: ch2, fn: fn2}); 15298 this.index += 2; 15299 } else if (fn) { 15300 this.tokens.push({ 15301 index: this.index, 15302 text: this.ch, 15303 fn: fn 15304 }); 15305 this.index += 1;
15306 } else { 15307 this.throwError('Unexpected next character ', this.index, this.index + 1); 15308 } 15309 } 15310 this.lastCh = this.ch; 15311 } 15312 return this.tokens; 15313 }, 15314 15315 is: function(chars) { 15316 return chars.indexOf(this.ch) !== -1; 15317 }, 15318 15319 was: function(chars) { 15320 return chars.indexOf(this.lastCh) !== -1; 15321 }, 15322 15323 peek: function(i) { 15324 var num = i || 1; 15325 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; 15326 }, 15327 15328 isNumber: function(ch) { 15329 return ('0' <= ch && ch <= '9'); 15330 }, 15331 15332 isWhitespace: function(ch) { 15333 // IE treats non-breaking space as \u00A0 15334 return (ch === ' ' || ch === '\r' || ch === '\t' || 15335 ch === '\n' || ch === '\v' || ch === '\u00A0'); 15336 }, 15337 15338 isIdent: function(ch) { 15339 return ('a' <= ch && ch <= 'z' || 15340 'A' <= ch && ch <= 'Z' || 15341 '_' === ch || ch === '$'); 15342 }, 15343 15344 isExpOperator: function(ch) { 15345 return (ch === '-' || ch === '+' || this.isNumber(ch)); 15346 }, 15347 15348 throwError: function(error, start, end) { 15349 end = end || this.index; 15350 var colStr = (isDefined(start) 15351 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' 15352 : ' ' + end); 15353 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', 15354 error, colStr, this.text); 15355 }, 15356 15357 readNumber: function() { 15358 var number = ''; 15359 var start = this.index; 15360 while (this.index < this.text.length) { 15361 var ch = lowercase(this.text.charAt(this.index)); 15362 if (ch == '.' || this.isNumber(ch)) { 15363 number += ch; 15364 } else { 15365 var peekCh = this.peek(); 15366 if (ch == 'e' && this.isExpOperator(peekCh)) { 15367 number += ch; 15368 } else if (this.isExpOperator(ch) && 15369 peekCh && this.isNumber(peekCh) && 15370 number.charAt(number.length - 1) == 'e') { 15371 number += ch; 15372 } else if (this.isExpOperator(ch) && 15373 (!peekCh || !this.isNumber(peekCh)) && 15374 number.charAt(number.length - 1) == 'e') { 15375 this.throwError('Invalid exponent'); 15376 } else { 15377 break; 15378 } 15379 } 15380 this.index++; 15381 } 15382 number = 1 * number; 15383 this.tokens.push({ 15384 index: start, 15385 text: number, 15386 literal: true, 15387 constant: true, 15388 fn: function() { return number; } 15389 }); 15390 }, 15391 15392 readIdent: function() { 15393 var parser = this; 15394 15395 var ident = ''; 15396 var start = this.index; 15397 15398 var lastDot, peekIndex, methodName, ch; 15399 15400 while (this.index < this.text.length) { 15401 ch = this.text.charAt(this.index); 15402 if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { 15403 if (ch === '.') lastDot = this.index; 15404 ident += ch; 15405 } else { 15406 break; 15407 } 15408 this.index++; 15409 } 15410 15411 //check if this is not a method invocation and if it is back out to last dot 15412 if (lastDot) { 15413 peekIndex = this.index; 15414 while (peekIndex < this.text.length) { 15415 ch = this.text.charAt(peekIndex); 15416 if (ch === '(') { 15417 methodName = ident.substr(lastDot - start + 1); 15418 ident = ident.substr(0, lastDot - start); 15419 this.index = peekIndex; 15420 break; 15421 } 15422 if (this.isWhitespace(ch)) { 15423 peekIndex++; 15424 } else { 15425 break; 15426 } 15427 } 15428 } 15429 15430 15431 var token = { 15432 index: start, 15433 text: ident 15434 }; 15435 15436 // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn 15437 if (OPERATORS.hasOwnProperty(ident)) { 15438 token.fn = OPERATORS[ident]; 15439 token.literal = true; 15440 token.constant = true; 15441 } else { 15442 var getter = getterFn(ident, this.options, this.text); 15443 token.fn = extend(function(self, locals) { 15444 return (getter(self, locals)); 15445 }, { 15446 assign: function(self, value) { 15447 return setter(self, ident, value, parser.text, parser.options); 15448 } 15449 }); 15450 } 15451 15452 this.tokens.push(token); 15453 15454 if (methodName) { 15455 this.tokens.push({ 15456 index:lastDot, 15457 text: '.' 15458 }); 15459 this.tokens.push({ 15460 index: lastDot + 1, 15461 text: methodName 15462 }); 15463 } 15464 }, 15465 15466 readString: function(quote) { 15467 var start = this.index; 15468 this.index++; 15469 var string = ''; 15470 var rawString = quote; 15471 var escape = false; 15472 while (this.index < this.text.length) { 15473 var ch = this.text.charAt(this.index); 15474 rawString += ch; 15475 if (escape) { 15476 if (ch === 'u') { 15477 var hex = this.text.substring(this.index + 1, this.index + 5); 15478 if (!hex.match(/[\da-f]{4}/i)) 15479 this.throwError('Invalid unicode escape [\\u' + hex + ']'); 15480 this.index += 4;
15481 string += String.fromCharCode(parseInt(hex, 16)); 15482 } else { 15483 var rep = ESCAPE[ch]; 15484 string = string + (rep || ch); 15485 } 15486 escape = false; 15487 } else if (ch === '\\') { 15488 escape = true; 15489 } else if (ch === quote) { 15490 this.index++; 15491 this.tokens.push({ 15492 index: start, 15493 text: rawString, 15494 string: string, 15495 literal: true, 15496 constant: true, 15497 fn: function() { return string; } 15498 }); 15499 return; 15500 } else { 15501 string += ch; 15502 } 15503 this.index++; 15504 } 15505 this.throwError('Unterminated quote', start); 15506 } 15507}; 15508 15509 15510/** 15511 * @constructor 15512 */ 15513var Parser = function (lexer, $filter, options) { 15514 this.lexer = lexer; 15515 this.$filter = $filter; 15516 this.options = options; 15517}; 15518 15519Parser.ZERO = extend(function () { 15520 return 0; 15521}, { 15522 constant: true 15523}); 15524 15525Parser.prototype = { 15526 constructor: Parser, 15527 15528 parse: function (text) { 15529 this.text = text; 15530 15531 this.tokens = this.lexer.lex(text); 15532 15533 var value = this.statements(); 15534 15535 if (this.tokens.length !== 0) { 15536 this.throwError('is an unexpected token', this.tokens[0]); 15537 } 15538 15539 value.literal = !!value.literal; 15540 value.constant = !!value.constant; 15541 15542 return value; 15543 }, 15544 15545 primary: function () { 15546 var primary; 15547 if (this.expect('(')) { 15548 primary = this.filterChain(); 15549 this.consume(')'); 15550 } else if (this.expect('[')) { 15551 primary = this.arrayDeclaration(); 15552 } else if (this.expect('{')) { 15553 primary = this.object(); 15554 } else { 15555 var token = this.expect(); 15556 primary = token.fn; 15557 if (!primary) { 15558 this.throwError('not a primary expression', token); 15559 } 15560 primary.literal = !!token.literal; 15561 primary.constant = !!token.constant; 15562 } 15563 15564 var next, context; 15565 while ((next = this.expect('(', '[', '.'))) { 15566 if (next.text === '(') { 15567 primary = this.functionCall(primary, context); 15568 context = null; 15569 } else if (next.text === '[') { 15570 context = primary; 15571 primary = this.objectIndex(primary); 15572 } else if (next.text === '.') { 15573 context = primary; 15574 primary = this.fieldAccess(primary); 15575 } else { 15576 this.throwError('IMPOSSIBLE'); 15577 } 15578 } 15579 return primary; 15580 }, 15581 15582 throwError: function(msg, token) { 15583 throw $parseMinErr('syntax', 15584 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', 15585 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); 15586 }, 15587 15588 peekToken: function() { 15589 if (this.tokens.length === 0) 15590 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); 15591 return this.tokens[0]; 15592 }, 15593 15594 peek: function(e1, e2, e3, e4) { 15595 if (this.tokens.length > 0) { 15596 var token = this.tokens[0]; 15597 var t = token.text; 15598 if (t === e1 || t === e2 || t === e3 || t === e4 || 15599 (!e1 && !e2 && !e3 && !e4)) { 15600 return token; 15601 } 15602 } 15603 return false; 15604 }, 15605 15606 expect: function(e1, e2, e3, e4){ 15607 var token = this.peek(e1, e2, e3, e4); 15608 if (token) { 15609 this.tokens.shift(); 15610 return token; 15611 } 15612 return false; 15613 }, 15614 15615 consume: function(e1){ 15616 if (!this.expect(e1)) { 15617 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); 15618 } 15619 }, 15620 15621 unaryFn: function(fn, right) { 15622 return extend(function(self, locals) { 15623 return fn(self, locals, right); 15624 }, { 15625 constant:right.constant 15626 }); 15627 }, 15628 15629 ternaryFn: function(left, middle, right){ 15630 return extend(function(self, locals){ 15631 return left(self, locals) ? middle(self, locals) : right(self, locals); 15632 }, { 15633 constant: left.constant && middle.constant && right.constant 15634 }); 15635 }, 15636 15637 binaryFn: function(left, fn, right) { 15638 return extend(function(self, locals) { 15639 return fn(self, locals, left, right); 15640 }, { 15641 constant:left.constant && right.constant 15642 });
15643 }, 15644 15645 statements: function() { 15646 var statements = []; 15647 while (true) { 15648 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) 15649 statements.push(this.filterChain()); 15650 if (!this.expect(';')) { 15651 // optimize for the common case where there is only one statement. 15652 // TODO(size): maybe we should not support multiple statements? 15653 return (statements.length === 1) 15654 ? statements[0] 15655 : function(self, locals) { 15656 var value; 15657 for (var i = 0; i < statements.length; i++) { 15658 var statement = statements[i]; 15659 if (statement) { 15660 value = statement(self, locals); 15661 } 15662 } 15663 return value; 15664 }; 15665 } 15666 } 15667 }, 15668 15669 filterChain: function() { 15670 var left = this.expression(); 15671 var token; 15672 while (true) { 15673 if ((token = this.expect('|'))) { 15674 left = this.binaryFn(left, token.fn, this.filter()); 15675 } else { 15676 return left; 15677 } 15678 } 15679 }, 15680 15681 filter: function() { 15682 var token = this.expect(); 15683 var fn = this.$filter(token.text); 15684 var argsFn = []; 15685 while (true) { 15686 if ((token = this.expect(':'))) { 15687 argsFn.push(this.expression()); 15688 } else { 15689 var fnInvoke = function(self, locals, input) { 15690 var args = [input]; 15691 for (var i = 0; i < argsFn.length; i++) { 15692 args.push(argsFn[i](self, locals)); 15693 } 15694 return fn.apply(self, args); 15695 }; 15696 return function() { 15697 return fnInvoke; 15698 }; 15699 } 15700 } 15701 }, 15702 15703 expression: function() { 15704 return this.assignment(); 15705 }, 15706 15707 assignment: function() { 15708 var left = this.ternary(); 15709 var right; 15710 var token; 15711 if ((token = this.expect('='))) { 15712 if (!left.assign) { 15713 this.throwError('implies assignment but [' + 15714 this.text.substring(0, token.index) + '] can not be assigned to', token); 15715 } 15716 right = this.ternary(); 15717 return function(scope, locals) { 15718 return left.assign(scope, right(scope, locals), locals); 15719 }; 15720 } 15721 return left; 15722 }, 15723 15724 ternary: function() { 15725 var left = this.logicalOR(); 15726 var middle; 15727 var token; 15728 if ((token = this.expect('?'))) { 15729 middle = this.assignment(); 15730 if ((token = this.expect(':'))) { 15731 return this.ternaryFn(left, middle, this.assignment()); 15732 } else { 15733 this.throwError('expected :', token); 15734 } 15735 } else { 15736 return left; 15737 } 15738 }, 15739 15740 logicalOR: function() { 15741 var left = this.logicalAND(); 15742 var token; 15743 while (true) { 15744 if ((token = this.expect('||'))) { 15745 left = this.binaryFn(left, token.fn, this.logicalAND()); 15746 } else { 15747 return left; 15748 } 15749 } 15750 }, 15751 15752 logicalAND: function() { 15753 var left = this.equality(); 15754 var token; 15755 if ((token = this.expect('&&'))) { 15756 left = this.binaryFn(left, token.fn, this.logicalAND()); 15757 } 15758 return left; 15759 }, 15760 15761 equality: function() { 15762 var left = this.relational(); 15763 var token; 15764 if ((token = this.expect('==','!=','===','!=='))) { 15765 left = this.binaryFn(left, token.fn, this.equality()); 15766 } 15767 return left; 15768 }, 15769 15770 relational: function() { 15771 var left = this.additive(); 15772 var token; 15773 if ((token = this.expect('<', '>', '<=', '>='))) { 15774 left = this.binaryFn(left, token.fn, this.relational()); 15775 } 15776 return left; 15777 }, 15778 15779 additive: function() { 15780 var left = this.multiplicative(); 15781 var token; 15782 while ((token = this.expect('+','-'))) { 15783 left = this.binaryFn(left, token.fn, this.multiplicative()); 15784 } 15785 return left; 15786 }, 15787 15788 multiplicative: function() { 15789 var left = this.unary(); 15790 var token; 15791 while ((token = this.expect('*','/','%'))) { 15792 left = this.binaryFn(left, token.fn, this.unary()); 15793 } 15794 return left; 15795 }, 15796 15797 unary: function() { 15798 var token; 15799 if (this.expect('+')) { 15800 return this.primary();
15801 } else if ((token = this.expect('-'))) { 15802 return this.binaryFn(Parser.ZERO, token.fn, this.unary()); 15803 } else if ((token = this.expect('!'))) { 15804 return this.unaryFn(token.fn, this.unary()); 15805 } else { 15806 return this.primary(); 15807 } 15808 }, 15809 15810 fieldAccess: function(object) { 15811 var parser = this; 15812 var field = this.expect().text; 15813 var getter = getterFn(field, this.options, this.text); 15814 15815 return extend(function(scope, locals, self) { 15816 return getter(self || object(scope, locals)); 15817 }, { 15818 assign: function(scope, value, locals) { 15819 var o = object(scope, locals); 15820 if (!o) object.assign(scope, o = {}); 15821 return setter(o, field, value, parser.text, parser.options); 15822 } 15823 }); 15824 }, 15825 15826 objectIndex: function(obj) { 15827 var parser = this; 15828 15829 var indexFn = this.expression(); 15830 this.consume(']'); 15831 15832 return extend(function(self, locals) { 15833 var o = obj(self, locals), 15834 i = indexFn(self, locals), 15835 v, p; 15836 15837 ensureSafeMemberName(i, parser.text); 15838 if (!o) return undefined; 15839 v = ensureSafeObject(o[i], parser.text); 15840 if (v && v.then && parser.options.unwrapPromises) { 15841 p = v; 15842 if (!('$$v' in v)) { 15843 p.$$v = undefined; 15844 p.then(function(val) { p.$$v = val; }); 15845 } 15846 v = v.$$v; 15847 } 15848 return v; 15849 }, { 15850 assign: function(self, value, locals) { 15851 var key = ensureSafeMemberName(indexFn(self, locals), parser.text); 15852 // prevent overwriting of Function.constructor which would break ensureSafeObject check 15853 var o = ensureSafeObject(obj(self, locals), parser.text); 15854 if (!o) obj.assign(self, o = {}); 15855 return o[key] = value; 15856 } 15857 }); 15858 }, 15859 15860 functionCall: function(fn, contextGetter) { 15861 var argsFn = []; 15862 if (this.peekToken().text !== ')') { 15863 do { 15864 argsFn.push(this.expression()); 15865 } while (this.expect(',')); 15866 } 15867 this.consume(')'); 15868 15869 var parser = this; 15870 15871 return function(scope, locals) { 15872 var args = []; 15873 var context = contextGetter ? contextGetter(scope, locals) : scope; 15874 15875 for (var i = 0; i < argsFn.length; i++) { 15876 args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text)); 15877 } 15878 var fnPtr = fn(scope, locals, context) || noop; 15879 15880 ensureSafeObject(context, parser.text); 15881 ensureSafeFunction(fnPtr, parser.text); 15882 15883 // IE stupidity! (IE doesn't have apply for some native functions) 15884 var v = fnPtr.apply 15885 ? fnPtr.apply(context, args) 15886 : fnPtr(args[0], args[1], args[2], args[3], args[4]); 15887 15888 return ensureSafeObject(v, parser.text); 15889 }; 15890 }, 15891 15892 // This is used with json array declaration 15893 arrayDeclaration: function () { 15894 var elementFns = []; 15895 var allConstant = true; 15896 if (this.peekToken().text !== ']') { 15897 do { 15898 if (this.peek(']')) { 15899 // Support trailing commas per ES5.1. 15900 break; 15901 } 15902 var elementFn = this.expression(); 15903 elementFns.push(elementFn); 15904 if (!elementFn.constant) { 15905 allConstant = false; 15906 } 15907 } while (this.expect(',')); 15908 } 15909 this.consume(']'); 15910 15911 return extend(function(self, locals) { 15912 var array = []; 15913 for (var i = 0; i < elementFns.length; i++) { 15914 array.push(elementFns[i](self, locals)); 15915 } 15916 return array; 15917 }, { 15918 literal: true, 15919 constant: allConstant 15920 }); 15921 }, 15922 15923 object: function () { 15924 var keyValues = []; 15925 var allConstant = true; 15926 if (this.peekToken().text !== '}') { 15927 do { 15928 if (this.peek('}')) { 15929 // Support trailing commas per ES5.1. 15930 break; 15931 } 15932 var token = this.expect(), 15933 key = token.string || token.text; 15934 this.consume(':'); 15935 var value = this.expression(); 15936 keyValues.push({key: key, value: value}); 15937 if (!value.constant) { 15938 allConstant = false; 15939 } 15940 } while (this.expect(',')); 15941 } 15942 this.consume('}'); 15943 15944 return extend(function(self, locals) { 15945 var object = {}; 15946 for (var i = 0; i < keyValues.length; i++) { 15947 var keyValue = keyValues[i]; 15948 object[keyValue.key] = keyValue.value(self, locals); 15949 } 15950 return object; 15951 }, { 15952 literal: true, 15953 constant: allConstant 15954 });
15955 } 15956}; 15957 15958 15959////////////////////////////////////////////////// 15960// Parser helper functions 15961////////////////////////////////////////////////// 15962 15963function setter(obj, path, setValue, fullExp, options) { 15964 ensureSafeObject(obj, fullExp); 15965 15966 //needed? 15967 options = options || {}; 15968 15969 var element = path.split('.'), key; 15970 for (var i = 0; element.length > 1; i++) { 15971 key = ensureSafeMemberName(element.shift(), fullExp); 15972 var propertyObj = ensureSafeObject(obj[key], fullExp); 15973 if (!propertyObj) { 15974 propertyObj = {}; 15975 obj[key] = propertyObj; 15976 } 15977 obj = propertyObj; 15978 if (obj.then && options.unwrapPromises) { 15979 promiseWarning(fullExp); 15980 if (!("$$v" in obj)) { 15981 (function(promise) { 15982 promise.then(function(val) { promise.$$v = val; }); } 15983 )(obj); 15984 } 15985 if (obj.$$v === undefined) { 15986 obj.$$v = {}; 15987 } 15988 obj = obj.$$v; 15989 } 15990 } 15991 key = ensureSafeMemberName(element.shift(), fullExp); 15992 ensureSafeObject(obj[key], fullExp); 15993 obj[key] = setValue; 15994 return setValue; 15995} 15996 15997var getterFnCache = {}; 15998 15999/** 16000 * Implementation of the "Black Hole" variant from: 16001 * - http://jsperf.com/angularjs-parse-getter/4 16002 * - http://jsperf.com/path-evaluation-simplified/7 16003 */ 16004function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) { 16005 ensureSafeMemberName(key0, fullExp); 16006 ensureSafeMemberName(key1, fullExp); 16007 ensureSafeMemberName(key2, fullExp); 16008 ensureSafeMemberName(key3, fullExp); 16009 ensureSafeMemberName(key4, fullExp); 16010 16011 return !options.unwrapPromises 16012 ? function cspSafeGetter(scope, locals) { 16013 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; 16014 16015 if (pathVal == null) return pathVal; 16016 pathVal = pathVal[key0]; 16017 16018 if (!key1) return pathVal; 16019 if (pathVal == null) return undefined; 16020 pathVal = pathVal[key1]; 16021 16022 if (!key2) return pathVal; 16023 if (pathVal == null) return undefined; 16024 pathVal = pathVal[key2]; 16025 16026 if (!key3) return pathVal; 16027 if (pathVal == null) return undefined; 16028 pathVal = pathVal[key3]; 16029 16030 if (!key4) return pathVal; 16031 if (pathVal == null) return undefined; 16032 pathVal = pathVal[key4]; 16033 16034 return pathVal; 16035 } 16036 : function cspSafePromiseEnabledGetter(scope, locals) { 16037 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, 16038 promise; 16039 16040 if (pathVal == null) return pathVal; 16041 16042 pathVal = pathVal[key0]; 16043 if (pathVal && pathVal.then) { 16044 promiseWarning(fullExp); 16045 if (!("$$v" in pathVal)) { 16046 promise = pathVal; 16047 promise.$$v = undefined; 16048 promise.then(function(val) { promise.$$v = val; }); 16049 } 16050 pathVal = pathVal.$$v; 16051 } 16052 16053 if (!key1) return pathVal; 16054 if (pathVal == null) return undefined; 16055 pathVal = pathVal[key1]; 16056 if (pathVal && pathVal.then) { 16057 promiseWarning(fullExp); 16058 if (!("$$v" in pathVal)) { 16059 promise = pathVal; 16060 promise.$$v = undefined; 16061 promise.then(function(val) { promise.$$v = val; }); 16062 } 16063 pathVal = pathVal.$$v; 16064 } 16065 16066 if (!key2) return pathVal; 16067 if (pathVal == null) return undefined; 16068 pathVal = pathVal[key2]; 16069 if (pathVal && pathVal.then) { 16070 promiseWarning(fullExp); 16071 if (!("$$v" in pathVal)) { 16072 promise = pathVal; 16073 promise.$$v = undefined; 16074 promise.then(function(val) { promise.$$v = val; }); 16075 } 16076 pathVal = pathVal.$$v; 16077 } 16078 16079 if (!key3) return pathVal; 16080 if (pathVal == null) return undefined; 16081 pathVal = pathVal[key3]; 16082 if (pathVal && pathVal.then) { 16083 promiseWarning(fullExp); 16084 if (!("$$v" in pathVal)) { 16085 promise = pathVal; 16086 promise.$$v = undefined; 16087 promise.then(function(val) { promise.$$v = val; }); 16088 } 16089 pathVal = pathVal.$$v; 16090 } 16091 16092 if (!key4) return pathVal; 16093 if (pathVal == null) return undefined; 16094 pathVal = pathVal[key4]; 16095 if (pathVal && pathVal.then) { 16096 promiseWarning(fullExp); 16097 if (!("$$v" in pathVal)) { 16098 promise = pathVal; 16099 promise.$$v = undefined; 16100 promise.then(function(val) { promise.$$v = val; }); 16101 } 16102 pathVal = pathVal.$$v; 16103 } 16104 return pathVal; 16105 }; 16106} 16107 16108function getterFn(path, options, fullExp) { 16109 // Check whether the cache has this getter already. 16110 // We can use hasOwnProperty directly on the cache because we ensure, 16111 // see below, that the cache never stores a path called 'hasOwnProperty' 16112 if (getterFnCache.hasOwnProperty(path)) { 16113 return getterFnCache[path]; 16114 } 16115 16116 var pathKeys = path.split('.'), 16117 pathKeysLength = pathKeys.length, 16118 fn; 16119 16120 // http://jsperf.com/angularjs-parse-getter/6 16121 if (options.csp) { 16122 if (pathKeysLength < 6) { 16123 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, 16124 options); 16125 } else { 16126 fn = function(scope, locals) { 16127 var i = 0, val; 16128 do { 16129 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], 16130 pathKeys[i++], fullExp, options)(scope, locals); 16131 16132 locals = undefined; // clear after first iteration 16133 scope = val; 16134 } while (i < pathKeysLength); 16135 return val; 16136 }; 16137 } 16138 } else { 16139 var code = 'var p;\n';
16140 forEach(pathKeys, function(key, index) { 16141 ensureSafeMemberName(key, fullExp); 16142 code += 'if(s == null) return undefined;\n' + 16143 's='+ (index 16144 // we simply dereference 's' on any .dot notation 16145 ? 's' 16146 // but if we are first then we check locals first, and if so read it first 16147 : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + 16148 (options.unwrapPromises 16149 ? 'if (s && s.then) {\n' + 16150 ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' + 16151 ' if (!("$$v" in s)) {\n' + 16152 ' p=s;\n' + 16153 ' p.$$v = undefined;\n' + 16154 ' p.then(function(v) {p.$$v=v;});\n' + 16155 '}\n' + 16156 ' s=s.$$v\n' + 16157 '}\n' 16158 : ''); 16159 }); 16160 code += 'return s;'; 16161 16162 /* jshint -W054 */ 16163 var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning 16164 /* jshint +W054 */ 16165 evaledFnGetter.toString = valueFn(code); 16166 fn = options.unwrapPromises ? function(scope, locals) { 16167 return evaledFnGetter(scope, locals, promiseWarning); 16168 } : evaledFnGetter; 16169 } 16170 16171 // Only cache the value if it's not going to mess up the cache object 16172 // This is more performant that using Object.prototype.hasOwnProperty.call 16173 if (path !== 'hasOwnProperty') { 16174 getterFnCache[path] = fn; 16175 } 16176 return fn; 16177} 16178 16179/////////////////////////////////// 16180 16181/** 16182 * @ngdoc service 16183 * @name $parse 16184 * @kind function 16185 * 16186 * @description 16187 * 16188 * Converts Angular {@link guide/expression expression} into a function. 16189 * 16190 * ```js 16191 * var getter = $parse('user.name'); 16192 * var setter = getter.assign; 16193 * var context = {user:{name:'angular'}}; 16194 * var locals = {user:{name:'local'}}; 16195 * 16196 * expect(getter(context)).toEqual('angular'); 16197 * setter(context, 'newValue'); 16198 * expect(context.user.name).toEqual('newValue'); 16199 * expect(getter(context, locals)).toEqual('local'); 16200 * ``` 16201 * 16202 * 16203 * @param {string} expression String expression to compile. 16204 * @returns {function(context, locals)} a function which represents the compiled expression: 16205 * 16206 * * `context` â `{object}` â an object against which any expressions embedded in the strings 16207 * are evaluated against (typically a scope object). 16208 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 16209 * `context`. 16210 * 16211 * The returned function also has the following properties: 16212 * * `literal` â `{boolean}` â whether the expression's top-level node is a JavaScript 16213 * literal. 16214 * * `constant` â `{boolean}` â whether the expression is made entirely of JavaScript 16215 * constant literals. 16216 * * `assign` â `{?function(context, value)}` â if the expression is assignable, this will be 16217 * set to a function to change its value on the given context. 16218 * 16219 */ 16220 16221 16222/** 16223 * @ngdoc provider 16224 * @name $parseProvider 16225 * @kind function 16226 * 16227 * @description 16228 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} 16229 * service. 16230 */ 16231function $ParseProvider() { 16232 var cache = {}; 16233 16234 var $parseOptions = { 16235 csp: false, 16236 unwrapPromises: false, 16237 logPromiseWarnings: true 16238 }; 16239 16240 16241 /** 16242 * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. 16243 * 16244 * @ngdoc method 16245 * @name $parseProvider#unwrapPromises 16246 * @description 16247 * 16248 * **This feature is deprecated, see deprecation notes below for more info** 16249 * 16250 * If set to true (default is false), $parse will unwrap promises automatically when a promise is 16251 * found at any part of the expression. In other words, if set to true, the expression will always 16252 * result in a non-promise value. 16253 * 16254 * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, 16255 * the fulfillment value is used in place of the promise while evaluating the expression. 16256 * 16257 * **Deprecation notice** 16258 * 16259 * This is a feature that didn't prove to be wildly useful or popular, primarily because of the 16260 * dichotomy between data access in templates (accessed as raw values) and controller code 16261 * (accessed as promises). 16262 * 16263 * In most code we ended up resolving promises manually in controllers anyway and thus unifying 16264 * the model access there. 16265 * 16266 * Other downsides of automatic promise unwrapping: 16267 *
16268 * - when building components it's often desirable to receive the raw promises 16269 * - adds complexity and slows down expression evaluation 16270 * - makes expression code pre-generation unattractive due to the amount of code that needs to be 16271 * generated 16272 * - makes IDE auto-completion and tool support hard 16273 * 16274 * **Warning Logs** 16275 * 16276 * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a 16277 * promise (to reduce the noise, each expression is logged only once). To disable this logging use 16278 * `$parseProvider.logPromiseWarnings(false)` api. 16279 * 16280 * 16281 * @param {boolean=} value New value. 16282 * @returns {boolean|self} Returns the current setting when used as getter and self if used as 16283 * setter. 16284 */ 16285 this.unwrapPromises = function(value) { 16286 if (isDefined(value)) { 16287 $parseOptions.unwrapPromises = !!value; 16288 return this; 16289 } else { 16290 return $parseOptions.unwrapPromises; 16291 } 16292 }; 16293 16294 16295 /** 16296 * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. 16297 * 16298 * @ngdoc method 16299 * @name $parseProvider#logPromiseWarnings 16300 * @description 16301 * 16302 * Controls whether Angular should log a warning on any encounter of a promise in an expression. 16303 * 16304 * The default is set to `true`. 16305 * 16306 * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well. 16307 * 16308 * @param {boolean=} value New value. 16309 * @returns {boolean|self} Returns the current setting when used as getter and self if used as 16310 * setter. 16311 */ 16312 this.logPromiseWarnings = function(value) { 16313 if (isDefined(value)) { 16314 $parseOptions.logPromiseWarnings = value; 16315 return this; 16316 } else { 16317 return $parseOptions.logPromiseWarnings; 16318 } 16319 }; 16320 16321 16322 this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) { 16323 $parseOptions.csp = $sniffer.csp; 16324 16325 promiseWarning = function promiseWarningFn(fullExp) { 16326 if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return; 16327 promiseWarningCache[fullExp] = true; 16328 $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' + 16329 'Automatic unwrapping of promises in Angular expressions is deprecated.'); 16330 }; 16331 16332 return function(exp) { 16333 var parsedExpression; 16334 16335 switch (typeof exp) { 16336 case 'string': 16337 16338 if (cache.hasOwnProperty(exp)) { 16339 return cache[exp]; 16340 } 16341 16342 var lexer = new Lexer($parseOptions); 16343 var parser = new Parser(lexer, $filter, $parseOptions); 16344 parsedExpression = parser.parse(exp); 16345 16346 if (exp !== 'hasOwnProperty') { 16347 // Only cache the value if it's not going to mess up the cache object 16348 // This is more performant that using Object.prototype.hasOwnProperty.call 16349 cache[exp] = parsedExpression; 16350 } 16351 16352 return parsedExpression; 16353 16354 case 'function': 16355 return exp; 16356 16357 default: 16358 return noop; 16359 } 16360 }; 16361 }]; 16362} 16363 16364/** 16365 * @ngdoc service 16366 * @name $q 16367 * @requires $rootScope 16368 * 16369 * @description 16370 * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). 16371 * 16372 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an 16373 * interface for interacting with an object that represents the result of an action that is 16374 * performed asynchronously, and may or may not be finished at any given point in time. 16375 * 16376 * From the perspective of dealing with error handling, deferred and promise APIs are to 16377 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. 16378 * 16379 * ```js 16380 * // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet` 16381 * // are available in the current lexical scope (they could have been injected or passed in). 16382 * 16383 * function asyncGreet(name) { 16384 * var deferred = $q.defer(); 16385 * 16386 * setTimeout(function() { 16387 * deferred.notify('About to greet ' + name + '.'); 16388 * 16389 * if (okToGreet(name)) { 16390 * deferred.resolve('Hello, ' + name + '!'); 16391 * } else { 16392 * deferred.reject('Greeting ' + name + ' is not allowed.'); 16393 * } 16394 * }, 1000); 16395 * 16396 * return deferred.promise; 16397 * } 16398 * 16399 * var promise = asyncGreet('Robin Hood'); 16400 * promise.then(function(greeting) { 16401 * alert('Success: ' + greeting); 16402 * }, function(reason) { 16403 * alert('Failed: ' + reason); 16404 * }, function(update) { 16405 * alert('Got notification: ' + update); 16406 * }); 16407 * ``` 16408 * 16409 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff 16410 * comes in the way of guarantees that promise and deferred APIs make, see 16411 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. 16412 *
16413 * Additionally the promise api allows for composition that is very hard to do with the 16414 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. 16415 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the 16416 * section on serial or parallel joining of promises. 16417 * 16418 * 16419 * # The Deferred API 16420 * 16421 * A new instance of deferred is constructed by calling `$q.defer()`. 16422 * 16423 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs 16424 * that can be used for signaling the successful or unsuccessful completion, as well as the status 16425 * of the task. 16426 * 16427 * **Methods** 16428 * 16429 * - `resolve(value)` â resolves the derived promise with the `value`. If the value is a rejection 16430 * constructed via `$q.reject`, the promise will be rejected instead. 16431 * - `reject(reason)` â rejects the derived promise with the `reason`. This is equivalent to 16432 * resolving it with a rejection constructed via `$q.reject`. 16433 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called 16434 * multiple times before the promise is either resolved or rejected. 16435 * 16436 * **Properties** 16437 * 16438 * - promise â `{Promise}` â promise object associated with this deferred. 16439 * 16440 * 16441 * # The Promise API 16442 * 16443 * A new promise instance is created when a deferred instance is created and can be retrieved by 16444 * calling `deferred.promise`. 16445 * 16446 * The purpose of the promise object is to allow for interested parties to get access to the result 16447 * of the deferred task when it completes. 16448 * 16449 * **Methods** 16450 * 16451 * - `then(successCallback, errorCallback, notifyCallback)` â regardless of when the promise was or 16452 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously 16453 * as soon as the result is available. The callbacks are called with a single argument: the result 16454 * or rejection reason. Additionally, the notify callback may be called zero or more times to 16455 * provide a progress indication, before the promise is resolved or rejected. 16456 * 16457 * This method *returns a new promise* which is resolved or rejected via the return value of the 16458 * `successCallback`, `errorCallback`. It also notifies via the return value of the 16459 * `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback 16460 * method. 16461 * 16462 * - `catch(errorCallback)` â shorthand for `promise.then(null, errorCallback)` 16463 * 16464 * - `finally(callback)` â allows you to observe either the fulfillment or rejection of a promise, 16465 * but to do so without modifying the final value. This is useful to release resources or do some 16466 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full 16467 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for 16468 * more information. 16469 * 16470 * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as 16471 * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to 16472 * make your code IE8 and Android 2.x compatible. 16473 * 16474 * # Chaining promises 16475 * 16476 * Because calling the `then` method of a promise returns a new derived promise, it is easily 16477 * possible to create a chain of promises: 16478 * 16479 * ```js 16480 * promiseB = promiseA.then(function(result) { 16481 * return result + 1; 16482 * }); 16483 * 16484 * // promiseB will be resolved immediately after promiseA is resolved and its value 16485 * // will be the result of promiseA incremented by 1 16486 * ``` 16487 * 16488 * It is possible to create chains of any length and since a promise can be resolved with another 16489 * promise (which will defer its resolution further), it is possible to pause/defer resolution of 16490 * the promises at any point in the chain. This makes it possible to implement powerful APIs like 16491 * $http's response interceptors. 16492 * 16493 * 16494 * # Differences between Kris Kowal's Q and $q 16495 * 16496 * There are two main differences: 16497 *
16498 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation 16499 * mechanism in angular, which means faster propagation of resolution or rejection into your 16500 * models and avoiding unnecessary browser repaints, which would result in flickering UI. 16501 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains 16502 * all the important functionality needed for common async tasks. 16503 * 16504 * # Testing 16505 * 16506 * ```js 16507 * it('should simulate promise', inject(function($q, $rootScope) { 16508 * var deferred = $q.defer(); 16509 * var promise = deferred.promise; 16510 * var resolvedValue; 16511 * 16512 * promise.then(function(value) { resolvedValue = value; }); 16513 * expect(resolvedValue).toBeUndefined(); 16514 * 16515 * // Simulate resolving of promise 16516 * deferred.resolve(123); 16517 * // Note that the 'then' function does not get called synchronously. 16518 * // This is because we want the promise API to always be async, whether or not 16519 * // it got called synchronously or asynchronously. 16520 * expect(resolvedValue).toBeUndefined(); 16521 * 16522 * // Propagate promise resolution to 'then' functions using $apply(). 16523 * $rootScope.$apply(); 16524 * expect(resolvedValue).toEqual(123); 16525 * })); 16526 * ``` 16527 */ 16528function $QProvider() { 16529 16530 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { 16531 return qFactory(function(callback) { 16532 $rootScope.$evalAsync(callback); 16533 }, $exceptionHandler); 16534 }]; 16535} 16536 16537 16538/** 16539 * Constructs a promise manager. 16540 * 16541 * @param {function(Function)} nextTick Function for executing functions in the next turn. 16542 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for 16543 * debugging purposes. 16544 * @returns {object} Promise manager. 16545 */ 16546function qFactory(nextTick, exceptionHandler) { 16547 16548 /** 16549 * @ngdoc method 16550 * @name $q#defer 16551 * @kind function 16552 * 16553 * @description 16554 * Creates a `Deferred` object which represents a task which will finish in the future. 16555 * 16556 * @returns {Deferred} Returns a new instance of deferred. 16557 */ 16558 var defer = function() { 16559 var pending = [], 16560 value, deferred; 16561 16562 deferred = { 16563 16564 resolve: function(val) { 16565 if (pending) { 16566 var callbacks = pending; 16567 pending = undefined; 16568 value = ref(val); 16569 16570 if (callbacks.length) { 16571 nextTick(function() { 16572 var callback; 16573 for (var i = 0, ii = callbacks.length; i < ii; i++) { 16574 callback = callbacks[i]; 16575 value.then(callback[0], callback[1], callback[2]); 16576 } 16577 }); 16578 } 16579 } 16580 }, 16581 16582 16583 reject: function(reason) { 16584 deferred.resolve(createInternalRejectedPromise(reason)); 16585 }, 16586 16587 16588 notify: function(progress) { 16589 if (pending) { 16590 var callbacks = pending; 16591 16592 if (pending.length) { 16593 nextTick(function() { 16594 var callback; 16595 for (var i = 0, ii = callbacks.length; i < ii; i++) { 16596 callback = callbacks[i]; 16597 callback[2](progress); 16598 } 16599 }); 16600 } 16601 } 16602 }, 16603 16604 16605 promise: { 16606 then: function(callback, errback, progressback) { 16607 var result = defer(); 16608 16609 var wrappedCallback = function(value) { 16610 try { 16611 result.resolve((isFunction(callback) ? callback : defaultCallback)(value)); 16612 } catch(e) { 16613 result.reject(e); 16614 exceptionHandler(e); 16615 } 16616 }; 16617 16618 var wrappedErrback = function(reason) { 16619 try { 16620 result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); 16621 } catch(e) { 16622 result.reject(e); 16623 exceptionHandler(e); 16624 } 16625 }; 16626 16627 var wrappedProgressback = function(progress) { 16628 try { 16629 result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress)); 16630 } catch(e) { 16631 exceptionHandler(e); 16632 } 16633 }; 16634 16635 if (pending) { 16636 pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]); 16637 } else { 16638 value.then(wrappedCallback, wrappedErrback, wrappedProgressback); 16639 } 16640 16641 return result.promise; 16642 }, 16643 16644 "catch": function(callback) { 16645 return this.then(null, callback); 16646 }, 16647 16648 "finally": function(callback) { 16649 16650 function makePromise(value, resolved) { 16651 var result = defer(); 16652 if (resolved) { 16653 result.resolve(value); 16654 } else { 16655 result.reject(value); 16656 } 16657 return result.promise; 16658 } 16659 16660 function handleCallback(value, isResolved) { 16661 var callbackOutput = null; 16662 try { 16663 callbackOutput = (callback ||defaultCallback)(); 16664 } catch(e) { 16665 return makePromise(e, false); 16666 }
16667 if (isPromiseLike(callbackOutput)) { 16668 return callbackOutput.then(function() { 16669 return makePromise(value, isResolved); 16670 }, function(error) { 16671 return makePromise(error, false); 16672 }); 16673 } else { 16674 return makePromise(value, isResolved); 16675 } 16676 } 16677 16678 return this.then(function(value) { 16679 return handleCallback(value, true); 16680 }, function(error) { 16681 return handleCallback(error, false); 16682 }); 16683 } 16684 } 16685 }; 16686 16687 return deferred; 16688 }; 16689 16690 16691 var ref = function(value) { 16692 if (isPromiseLike(value)) return value; 16693 return { 16694 then: function(callback) { 16695 var result = defer(); 16696 nextTick(function() { 16697 result.resolve(callback(value)); 16698 }); 16699 return result.promise; 16700 } 16701 }; 16702 }; 16703 16704 16705 /** 16706 * @ngdoc method 16707 * @name $q#reject 16708 * @kind function 16709 * 16710 * @description 16711 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be 16712 * used to forward rejection in a chain of promises. If you are dealing with the last promise in 16713 * a promise chain, you don't need to worry about it. 16714 * 16715 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of 16716 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via 16717 * a promise error callback and you want to forward the error to the promise derived from the 16718 * current promise, you have to "rethrow" the error by returning a rejection constructed via 16719 * `reject`. 16720 * 16721 * ```js 16722 * promiseB = promiseA.then(function(result) { 16723 * // success: do something and resolve promiseB 16724 * // with the old or a new result 16725 * return result; 16726 * }, function(reason) { 16727 * // error: handle the error if possible and 16728 * // resolve promiseB with newPromiseOrValue, 16729 * // otherwise forward the rejection to promiseB 16730 * if (canHandle(reason)) { 16731 * // handle the error and recover 16732 * return newPromiseOrValue; 16733 * } 16734 * return $q.reject(reason); 16735 * }); 16736 * ``` 16737 * 16738 * @param {*} reason Constant, message, exception or an object representing the rejection reason. 16739 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. 16740 */ 16741 var reject = function(reason) { 16742 var result = defer(); 16743 result.reject(reason); 16744 return result.promise; 16745 }; 16746 16747 var createInternalRejectedPromise = function(reason) { 16748 return { 16749 then: function(callback, errback) { 16750 var result = defer(); 16751 nextTick(function() { 16752 try { 16753 result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); 16754 } catch(e) { 16755 result.reject(e); 16756 exceptionHandler(e); 16757 } 16758 }); 16759 return result.promise; 16760 } 16761 }; 16762 }; 16763 16764 16765 /** 16766 * @ngdoc method 16767 * @name $q#when 16768 * @kind function 16769 * 16770 * @description 16771 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. 16772 * This is useful when you are dealing with an object that might or might not be a promise, or if 16773 * the promise comes from a source that can't be trusted. 16774 * 16775 * @param {*} value Value or a promise 16776 * @returns {Promise} Returns a promise of the passed value or promise 16777 */ 16778 var when = function(value, callback, errback, progressback) { 16779 var result = defer(), 16780 done; 16781 16782 var wrappedCallback = function(value) { 16783 try { 16784 return (isFunction(callback) ? callback : defaultCallback)(value); 16785 } catch (e) { 16786 exceptionHandler(e); 16787 return reject(e); 16788 } 16789 }; 16790 16791 var wrappedErrback = function(reason) { 16792 try { 16793 return (isFunction(errback) ? errback : defaultErrback)(reason); 16794 } catch (e) { 16795 exceptionHandler(e); 16796 return reject(e); 16797 } 16798 }; 16799 16800 var wrappedProgressback = function(progress) {
16801 try { 16802 return (isFunction(progressback) ? progressback : defaultCallback)(progress); 16803 } catch (e) { 16804 exceptionHandler(e); 16805 } 16806 }; 16807 16808 nextTick(function() { 16809 ref(value).then(function(value) { 16810 if (done) return; 16811 done = true; 16812 result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback)); 16813 }, function(reason) { 16814 if (done) return; 16815 done = true; 16816 result.resolve(wrappedErrback(reason)); 16817 }, function(progress) { 16818 if (done) return; 16819 result.notify(wrappedProgressback(progress)); 16820 }); 16821 }); 16822 16823 return result.promise; 16824 }; 16825 16826 16827 function defaultCallback(value) { 16828 return value; 16829 } 16830 16831 16832 function defaultErrback(reason) { 16833 return reject(reason); 16834 } 16835 16836 16837 /** 16838 * @ngdoc method 16839 * @name $q#all 16840 * @kind function 16841 * 16842 * @description 16843 * Combines multiple promises into a single promise that is resolved when all of the input 16844 * promises are resolved. 16845 * 16846 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. 16847 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, 16848 * each value corresponding to the promise at the same index/key in the `promises` array/hash. 16849 * If any of the promises is resolved with a rejection, this resulting promise will be rejected 16850 * with the same rejection value. 16851 */ 16852 function all(promises) { 16853 var deferred = defer(), 16854 counter = 0, 16855 results = isArray(promises) ? [] : {}; 16856 16857 forEach(promises, function(promise, key) { 16858 counter++; 16859 ref(promise).then(function(value) { 16860 if (results.hasOwnProperty(key)) return; 16861 results[key] = value; 16862 if (!(--counter)) deferred.resolve(results); 16863 }, function(reason) { 16864 if (results.hasOwnProperty(key)) return; 16865 deferred.reject(reason); 16866 }); 16867 }); 16868 16869 if (counter === 0) { 16870 deferred.resolve(results); 16871 } 16872 16873 return deferred.promise; 16874 } 16875 16876 return { 16877 defer: defer, 16878 reject: reject, 16879 when: when, 16880 all: all 16881 }; 16882} 16883 16884function $$RAFProvider(){ //rAF 16885 this.$get = ['$window', '$timeout', function($window, $timeout) { 16886 var requestAnimationFrame = $window.requestAnimationFrame || 16887 $window.webkitRequestAnimationFrame || 16888 $window.mozRequestAnimationFrame; 16889 16890 var cancelAnimationFrame = $window.cancelAnimationFrame || 16891 $window.webkitCancelAnimationFrame || 16892 $window.mozCancelAnimationFrame || 16893 $window.webkitCancelRequestAnimationFrame; 16894 16895 var rafSupported = !!requestAnimationFrame; 16896 var raf = rafSupported 16897 ? function(fn) { 16898 var id = requestAnimationFrame(fn); 16899 return function() { 16900 cancelAnimationFrame(id); 16901 }; 16902 } 16903 : function(fn) { 16904 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 16905 return function() { 16906 $timeout.cancel(timer); 16907 }; 16908 }; 16909 16910 raf.supported = rafSupported; 16911 16912 return raf; 16913 }]; 16914} 16915 16916/** 16917 * DESIGN NOTES 16918 * 16919 * The design decisions behind the scope are heavily favored for speed and memory consumption. 16920 * 16921 * The typical use of scope is to watch the expressions, which most of the time return the same 16922 * value as last time so we optimize the operation. 16923 * 16924 * Closures construction is expensive in terms of speed as well as memory: 16925 * - No closures, instead use prototypical inheritance for API 16926 * - Internal state needs to be stored on scope directly, which means that private state is 16927 * exposed as $$____ properties 16928 * 16929 * Loop operations are optimized by using while(count--) { ... } 16930 * - this means that in order to keep the same order of execution as addition we have to add 16931 * items to the array at the beginning (unshift) instead of at the end (push) 16932 * 16933 * Child scopes are created and removed often 16934 * - Using an array would be slow since inserts in middle are expensive so we use linked list 16935 *
16936 * There are few watches then a lot of observers. This is why you don't want the observer to be 16937 * implemented in the same way as watch. Watch requires return of initialization function which 16938 * are expensive to construct. 16939 */ 16940 16941 16942/** 16943 * @ngdoc provider 16944 * @name $rootScopeProvider 16945 * @description 16946 * 16947 * Provider for the $rootScope service. 16948 */ 16949 16950/** 16951 * @ngdoc method 16952 * @name $rootScopeProvider#digestTtl 16953 * @description 16954 * 16955 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and 16956 * assuming that the model is unstable. 16957 * 16958 * The current default is 10 iterations. 16959 * 16960 * In complex applications it's possible that the dependencies between `$watch`s will result in 16961 * several digest iterations. However if an application needs more than the default 10 digest 16962 * iterations for its model to stabilize then you should investigate what is causing the model to 16963 * continuously change during the digest. 16964 * 16965 * Increasing the TTL could have performance implications, so you should not change it without 16966 * proper justification. 16967 * 16968 * @param {number} limit The number of digest iterations. 16969 */ 16970 16971 16972/** 16973 * @ngdoc service 16974 * @name $rootScope 16975 * @description 16976 * 16977 * Every application has a single root {@link ng.$rootScope.Scope scope}. 16978 * All other scopes are descendant scopes of the root scope. Scopes provide separation 16979 * between the model and the view, via a mechanism for watching the model for changes. 16980 * They also provide an event emission/broadcast and subscription facility. See the 16981 * {@link guide/scope developer guide on scopes}. 16982 */ 16983function $RootScopeProvider(){ 16984 var TTL = 10; 16985 var $rootScopeMinErr = minErr('$rootScope'); 16986 var lastDirtyWatch = null; 16987 16988 this.digestTtl = function(value) { 16989 if (arguments.length) { 16990 TTL = value; 16991 } 16992 return TTL; 16993 }; 16994 16995 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', 16996 function( $injector, $exceptionHandler, $parse, $browser) { 16997 16998 /** 16999 * @ngdoc type 17000 * @name $rootScope.Scope 17001 * 17002 * @description 17003 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the 17004 * {@link auto.$injector $injector}. Child scopes are created using the 17005 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when 17006 * compiled HTML template is executed.) 17007 * 17008 * Here is a simple scope snippet to show how you can interact with the scope. 17009 * ```html 17010 * <file src="./test/ng/rootScopeSpec.js" tag="docs1" /> 17011 * ``` 17012 * 17013 * # Inheritance 17014 * A scope can inherit from a parent scope, as in this example: 17015 * ```js 17016 var parent = $rootScope; 17017 var child = parent.$new(); 17018 17019 parent.salutation = "Hello"; 17020 child.name = "World"; 17021 expect(child.salutation).toEqual('Hello'); 17022 17023 child.salutation = "Welcome"; 17024 expect(child.salutation).toEqual('Welcome'); 17025 expect(parent.salutation).toEqual('Hello'); 17026 * ``` 17027 * 17028 * 17029 * @param {Object.<string, function()>=} providers Map of service factory which need to be 17030 * provided for the current scope. Defaults to {@link ng}. 17031 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should 17032 * append/override services provided by `providers`. This is handy 17033 * when unit-testing and having the need to override a default 17034 * service. 17035 * @returns {Object} Newly created scope. 17036 * 17037 */ 17038 function Scope() { 17039 this.$id = nextUid(); 17040 this.$$phase = this.$parent = this.$$watchers = 17041 this.$$nextSibling = this.$$prevSibling = 17042 this.$$childHead = this.$$childTail = null; 17043 this['this'] = this.$root = this; 17044 this.$$destroyed = false; 17045 this.$$asyncQueue = []; 17046 this.$$postDigestQueue = []; 17047 this.$$listeners = {}; 17048 this.$$listenerCount = {}; 17049 this.$$isolateBindings = {}; 17050 } 17051 17052 /** 17053 * @ngdoc property 17054 * @name $rootScope.Scope#$id 17055 *
17056 * @description 17057 * Unique scope ID (monotonically increasing) useful for debugging. 17058 */ 17059 17060 /** 17061 * @ngdoc property 17062 * @name $rootScope.Scope#$parent 17063 * 17064 * @description 17065 * Reference to the parent scope. 17066 */ 17067 17068 /** 17069 * @ngdoc property 17070 * @name $rootScope.Scope#$root 17071 * 17072 * @description 17073 * Reference to the root scope. 17074 */ 17075 17076 Scope.prototype = { 17077 constructor: Scope, 17078 /** 17079 * @ngdoc method 17080 * @name $rootScope.Scope#$new 17081 * @kind function 17082 * 17083 * @description 17084 * Creates a new child {@link ng.$rootScope.Scope scope}. 17085 * 17086 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. 17087 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. 17088 * 17089 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is 17090 * desired for the scope and its child scopes to be permanently detached from the parent and 17091 * thus stop participating in model change detection and listener notification by invoking. 17092 * 17093 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the 17094 * parent scope. The scope is isolated, as it can not see parent scope properties. 17095 * When creating widgets, it is useful for the widget to not accidentally read parent 17096 * state. 17097 * 17098 * @returns {Object} The newly created child scope. 17099 * 17100 */ 17101 $new: function(isolate) { 17102 var ChildScope, 17103 child; 17104 17105 if (isolate) { 17106 child = new Scope(); 17107 child.$root = this.$root; 17108 // ensure that there is just one async queue per $rootScope and its children 17109 child.$$asyncQueue = this.$$asyncQueue; 17110 child.$$postDigestQueue = this.$$postDigestQueue; 17111 } else { 17112 // Only create a child scope class if somebody asks for one, 17113 // but cache it to allow the VM to optimize lookups. 17114 if (!this.$$childScopeClass) { 17115 this.$$childScopeClass = function() { 17116 this.$$watchers = this.$$nextSibling = 17117 this.$$childHead = this.$$childTail = null; 17118 this.$$listeners = {}; 17119 this.$$listenerCount = {}; 17120 this.$id = nextUid(); 17121 this.$$childScopeClass = null; 17122 }; 17123 this.$$childScopeClass.prototype = this; 17124 } 17125 child = new this.$$childScopeClass(); 17126 } 17127 child['this'] = child; 17128 child.$parent = this; 17129 child.$$prevSibling = this.$$childTail; 17130 if (this.$$childHead) { 17131 this.$$childTail.$$nextSibling = child; 17132 this.$$childTail = child; 17133 } else { 17134 this.$$childHead = this.$$childTail = child; 17135 } 17136 return child; 17137 }, 17138 17139 /** 17140 * @ngdoc method 17141 * @name $rootScope.Scope#$watch 17142 * @kind function 17143 * 17144 * @description 17145 * Registers a `listener` callback to be executed whenever the `watchExpression` changes. 17146 * 17147 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest 17148 * $digest()} and should return the value that will be watched. (Since 17149 * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the 17150 * `watchExpression` can execute multiple times per 17151 * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) 17152 * - The `listener` is called only when the value from the current `watchExpression` and the 17153 * previous call to `watchExpression` are not equal (with the exception of the initial run, 17154 * see below). Inequality is determined according to reference inequality, 17155 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) 17156 * via the `!==` Javascript operator, unless `objectEquality == true` 17157 * (see next point) 17158 * - When `objectEquality == true`, inequality of the `watchExpression` is determined 17159 * according to the {@link angular.equals} function. To save the value of the object for 17160 * later comparison, the {@link angular.copy} function is used. This therefore means that 17161 * watching complex objects will have adverse memory and performance implications. 17162 * - The watch `listener` may change the model, which may trigger other `listener`s to fire. 17163 * This is achieved by rerunning the watchers until no changes are detected. The rerun 17164 * iteration limit is 10 to prevent an infinite loop deadlock. 17165 * 17166 * 17167 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, 17168 * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` 17169 * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a 17170 * change is detected, be prepared for multiple calls to your listener.) 17171 * 17172 * After a watcher is registered with the scope, the `listener` fn is called asynchronously 17173 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the 17174 * watcher. In rare cases, this is undesirable because the listener is called when the result 17175 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
17176 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the 17177 * listener was called due to initialization. 17178 * 17179 * The example below contains an illustration of using a function as your $watch listener 17180 * 17181 * 17182 * # Example 17183 * ```js 17184 // let's assume that scope was dependency injected as the $rootScope 17185 var scope = $rootScope; 17186 scope.name = 'misko'; 17187 scope.counter = 0; 17188 17189 expect(scope.counter).toEqual(0); 17190 scope.$watch('name', function(newValue, oldValue) { 17191 scope.counter = scope.counter + 1; 17192 }); 17193 expect(scope.counter).toEqual(0); 17194 17195 scope.$digest(); 17196 // the listener is always called during the first $digest loop after it was registered 17197 expect(scope.counter).toEqual(1); 17198 17199 scope.$digest(); 17200 // but now it will not be called unless the value changes 17201 expect(scope.counter).toEqual(1); 17202 17203 scope.name = 'adam'; 17204 scope.$digest(); 17205 expect(scope.counter).toEqual(2); 17206 17207 17208 17209 // Using a listener function 17210 var food; 17211 scope.foodCounter = 0; 17212 expect(scope.foodCounter).toEqual(0); 17213 scope.$watch( 17214 // This is the listener function 17215 function() { return food; }, 17216 // This is the change handler 17217 function(newValue, oldValue) { 17218 if ( newValue !== oldValue ) { 17219 // Only increment the counter if the value changed 17220 scope.foodCounter = scope.foodCounter + 1; 17221 } 17222 } 17223 ); 17224 // No digest has been run so the counter will be zero 17225 expect(scope.foodCounter).toEqual(0); 17226 17227 // Run the digest but since food has not changed count will still be zero 17228 scope.$digest(); 17229 expect(scope.foodCounter).toEqual(0); 17230 17231 // Update food and run digest. Now the counter will increment 17232 food = 'cheeseburger'; 17233 scope.$digest(); 17234 expect(scope.foodCounter).toEqual(1); 17235 17236 * ``` 17237 * 17238 * 17239 * 17240 * @param {(function()|string)} watchExpression Expression that is evaluated on each 17241 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers 17242 * a call to the `listener`. 17243 * 17244 * - `string`: Evaluated as {@link guide/expression expression} 17245 * - `function(scope)`: called with current `scope` as a parameter. 17246 * @param {(function()|string)=} listener Callback called whenever the return value of 17247 * the `watchExpression` changes. 17248 * 17249 * - `string`: Evaluated as {@link guide/expression expression} 17250 * - `function(newValue, oldValue, scope)`: called with current and previous values as 17251 * parameters. 17252 * 17253 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of 17254 * comparing for reference equality. 17255 * @returns {function()} Returns a deregistration function for this listener. 17256 */ 17257 $watch: function(watchExp, listener, objectEquality) { 17258 var scope = this, 17259 get = compileToFn(watchExp, 'watch'), 17260 array = scope.$$watchers, 17261 watcher = { 17262 fn: listener, 17263 last: initWatchVal, 17264 get: get, 17265 exp: watchExp, 17266 eq: !!objectEquality 17267 }; 17268 17269 lastDirtyWatch = null; 17270 17271 // in the case user pass string, we need to compile it, do we really need this ? 17272 if (!isFunction(listener)) { 17273 var listenFn = compileToFn(listener || noop, 'listener'); 17274 watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; 17275 } 17276 17277 if (typeof watchExp == 'string' && get.constant) { 17278 var originalFn = watcher.fn; 17279 watcher.fn = function(newVal, oldVal, scope) { 17280 originalFn.call(this, newVal, oldVal, scope); 17281 arrayRemove(array, watcher); 17282 }; 17283 } 17284 17285 if (!array) { 17286 array = scope.$$watchers = []; 17287 } 17288 // we use unshift since we use a while loop in $digest for speed. 17289 // the while loop reads in reverse order. 17290 array.unshift(watcher); 17291 17292 return function deregisterWatch() { 17293 arrayRemove(array, watcher); 17294 lastDirtyWatch = null; 17295 }; 17296 }, 17297 17298 17299 /** 17300 * @ngdoc method 17301 * @name $rootScope.Scope#$watchCollection 17302 * @kind function 17303 *
17304 * @description 17305 * Shallow watches the properties of an object and fires whenever any of the properties change 17306 * (for arrays, this implies watching the array items; for object maps, this implies watching 17307 * the properties). If a change is detected, the `listener` callback is fired. 17308 * 17309 * - The `obj` collection is observed via standard $watch operation and is examined on every 17310 * call to $digest() to see if any items have been added, removed, or moved. 17311 * - The `listener` is called whenever anything within the `obj` has changed. Examples include 17312 * adding, removing, and moving items belonging to an object or array. 17313 * 17314 * 17315 * # Example 17316 * ```js 17317 $scope.names = ['igor', 'matias', 'misko', 'james']; 17318 $scope.dataCount = 4; 17319 17320 $scope.$watchCollection('names', function(newNames, oldNames) { 17321 $scope.dataCount = newNames.length; 17322 }); 17323 17324 expect($scope.dataCount).toEqual(4); 17325 $scope.$digest(); 17326 17327 //still at 4 ... no changes 17328 expect($scope.dataCount).toEqual(4); 17329 17330 $scope.names.pop(); 17331 $scope.$digest(); 17332 17333 //now there's been a change 17334 expect($scope.dataCount).toEqual(3); 17335 * ``` 17336 * 17337 * 17338 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The 17339 * expression value should evaluate to an object or an array which is observed on each 17340 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the 17341 * collection will trigger a call to the `listener`. 17342 * 17343 * @param {function(newCollection, oldCollection, scope)} listener a callback function called 17344 * when a change is detected. 17345 * - The `newCollection` object is the newly modified data obtained from the `obj` expression 17346 * - The `oldCollection` object is a copy of the former collection data. 17347 * Due to performance considerations, the`oldCollection` value is computed only if the 17348 * `listener` function declares two or more arguments. 17349 * - The `scope` argument refers to the current scope. 17350 * 17351 * @returns {function()} Returns a de-registration function for this listener. When the 17352 * de-registration function is executed, the internal watch operation is terminated. 17353 */ 17354 $watchCollection: function(obj, listener) { 17355 var self = this; 17356 // the current value, updated on each dirty-check run 17357 var newValue; 17358 // a shallow copy of the newValue from the last dirty-check run, 17359 // updated to match newValue during dirty-check run 17360 var oldValue; 17361 // a shallow copy of the newValue from when the last change happened 17362 var veryOldValue; 17363 // only track veryOldValue if the listener is asking for it 17364 var trackVeryOldValue = (listener.length > 1); 17365 var changeDetected = 0; 17366 var objGetter = $parse(obj); 17367 var internalArray = []; 17368 var internalObject = {}; 17369 var initRun = true; 17370 var oldLength = 0; 17371 17372 function $watchCollectionWatch() { 17373 newValue = objGetter(self); 17374 var newLength, key, bothNaN; 17375 17376 if (!isObject(newValue)) { // if primitive 17377 if (oldValue !== newValue) { 17378 oldValue = newValue; 17379 changeDetected++; 17380 } 17381 } else if (isArrayLike(newValue)) { 17382 if (oldValue !== internalArray) { 17383 // we are transitioning from something which was not an array into array. 17384 oldValue = internalArray; 17385 oldLength = oldValue.length = 0; 17386 changeDetected++; 17387 } 17388 17389 newLength = newValue.length; 17390 17391 if (oldLength !== newLength) { 17392 // if lengths do not match we need to trigger change notification 17393 changeDetected++; 17394 oldValue.length = oldLength = newLength; 17395 } 17396 // copy the items to oldValue and look for changes. 17397 for (var i = 0; i < newLength; i++) { 17398 bothNaN = (oldValue[i] !== oldValue[i]) && 17399 (newValue[i] !== newValue[i]);
17400 if (!bothNaN && (oldValue[i] !== newValue[i])) { 17401 changeDetected++; 17402 oldValue[i] = newValue[i]; 17403 } 17404 } 17405 } else { 17406 if (oldValue !== internalObject) { 17407 // we are transitioning from something which was not an object into object. 17408 oldValue = internalObject = {}; 17409 oldLength = 0; 17410 changeDetected++; 17411 } 17412 // copy the items to oldValue and look for changes. 17413 newLength = 0; 17414 for (key in newValue) { 17415 if (newValue.hasOwnProperty(key)) { 17416 newLength++; 17417 if (oldValue.hasOwnProperty(key)) { 17418 bothNaN = (oldValue[key] !== oldValue[key]) && 17419 (newValue[key] !== newValue[key]); 17420 if (!bothNaN && (oldValue[key] !== newValue[key])) { 17421 changeDetected++; 17422 oldValue[key] = newValue[key]; 17423 } 17424 } else { 17425 oldLength++; 17426 oldValue[key] = newValue[key]; 17427 changeDetected++; 17428 } 17429 } 17430 } 17431 if (oldLength > newLength) { 17432 // we used to have more keys, need to find them and destroy them. 17433 changeDetected++; 17434 for(key in oldValue) { 17435 if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { 17436 oldLength--; 17437 delete oldValue[key]; 17438 } 17439 } 17440 } 17441 } 17442 return changeDetected; 17443 } 17444 17445 function $watchCollectionAction() { 17446 if (initRun) { 17447 initRun = false; 17448 listener(newValue, newValue, self); 17449 } else { 17450 listener(newValue, veryOldValue, self); 17451 } 17452 17453 // make a copy for the next time a collection is changed 17454 if (trackVeryOldValue) { 17455 if (!isObject(newValue)) { 17456 //primitive 17457 veryOldValue = newValue; 17458 } else if (isArrayLike(newValue)) { 17459 veryOldValue = new Array(newValue.length); 17460 for (var i = 0; i < newValue.length; i++) { 17461 veryOldValue[i] = newValue[i]; 17462 } 17463 } else { // if object 17464 veryOldValue = {}; 17465 for (var key in newValue) { 17466 if (hasOwnProperty.call(newValue, key)) { 17467 veryOldValue[key] = newValue[key]; 17468 } 17469 } 17470 } 17471 } 17472 } 17473 17474 return this.$watch($watchCollectionWatch, $watchCollectionAction); 17475 }, 17476 17477 /** 17478 * @ngdoc method 17479 * @name $rootScope.Scope#$digest 17480 * @kind function 17481 * 17482 * @description 17483 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and 17484 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change 17485 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} 17486 * until no more listeners are firing. This means that it is possible to get into an infinite 17487 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of 17488 * iterations exceeds 10. 17489 * 17490 * Usually, you don't call `$digest()` directly in 17491 * {@link ng.directive:ngController controllers} or in 17492 * {@link ng.$compileProvider#directive directives}. 17493 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within 17494 * a {@link ng.$compileProvider#directive directives}), which will force a `$digest()`. 17495 * 17496 * If you want to be notified whenever `$digest()` is called, 17497 * you can register a `watchExpression` function with 17498 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. 17499 * 17500 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. 17501 * 17502 * # Example 17503 * ```js 17504 var scope = ...; 17505 scope.name = 'misko'; 17506 scope.counter = 0; 17507 17508 expect(scope.counter).toEqual(0); 17509 scope.$watch('name', function(newValue, oldValue) { 17510 scope.counter = scope.counter + 1; 17511 }); 17512 expect(scope.counter).toEqual(0); 17513 17514 scope.$digest(); 17515 // the listener is always called during the first $digest loop after it was registered 17516 expect(scope.counter).toEqual(1); 17517 17518 scope.$digest(); 17519 // but now it will not be called unless the value changes 17520 expect(scope.counter).toEqual(1); 17521 17522 scope.name = 'adam'; 17523 scope.$digest(); 17524 expect(scope.counter).toEqual(2); 17525 * ``` 17526 * 17527 */ 17528 $digest: function() { 17529 var watch, value, last, 17530 watchers, 17531 asyncQueue = this.$$asyncQueue, 17532 postDigestQueue = this.$$postDigestQueue, 17533 length, 17534 dirty, ttl = TTL, 17535 next, current, target = this, 17536 watchLog = [], 17537 logIdx, logMsg, asyncTask; 17538 17539 beginPhase('$digest'); 17540 // Check for changes to browser url that happened in sync before the call to $digest 17541 $browser.$$checkUrlChange(); 17542 17543 lastDirtyWatch = null; 17544
17545 do { // "while dirty" loop 17546 dirty = false; 17547 current = target; 17548 17549 while(asyncQueue.length) { 17550 try { 17551 asyncTask = asyncQueue.shift(); 17552 asyncTask.scope.$eval(asyncTask.expression); 17553 } catch (e) { 17554 clearPhase(); 17555 $exceptionHandler(e); 17556 } 17557 lastDirtyWatch = null; 17558 } 17559 17560 traverseScopesLoop: 17561 do { // "traverse the scopes" loop 17562 if ((watchers = current.$$watchers)) { 17563 // process our watches 17564 length = watchers.length; 17565 while (length--) { 17566 try { 17567 watch = watchers[length]; 17568 // Most common watches are on primitives, in which case we can short 17569 // circuit it with === operator, only when === fails do we use .equals 17570 if (watch) { 17571 if ((value = watch.get(current)) !== (last = watch.last) && 17572 !(watch.eq 17573 ? equals(value, last) 17574 : (typeof value === 'number' && typeof last === 'number' 17575 && isNaN(value) && isNaN(last)))) { 17576 dirty = true; 17577 lastDirtyWatch = watch; 17578 watch.last = watch.eq ? copy(value, null) : value; 17579 watch.fn(value, ((last === initWatchVal) ? value : last), current); 17580 if (ttl < 5) { 17581 logIdx = 4 - ttl; 17582 if (!watchLog[logIdx]) watchLog[logIdx] = []; 17583 logMsg = (isFunction(watch.exp)) 17584 ? 'fn: ' + (watch.exp.name || watch.exp.toString()) 17585 : watch.exp; 17586 logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); 17587 watchLog[logIdx].push(logMsg); 17588 } 17589 } else if (watch === lastDirtyWatch) { 17590 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers 17591 // have already been tested. 17592 dirty = false; 17593 break traverseScopesLoop; 17594 } 17595 } 17596 } catch (e) { 17597 clearPhase(); 17598 $exceptionHandler(e); 17599 } 17600 } 17601 } 17602 17603 // Insanity Warning: scope depth-first traversal 17604 // yes, this code is a bit crazy, but it works and we have tests to prove it! 17605 // this piece should be kept in sync with the traversal in $broadcast 17606 if (!(next = (current.$$childHead || 17607 (current !== target && current.$$nextSibling)))) { 17608 while(current !== target && !(next = current.$$nextSibling)) { 17609 current = current.$parent; 17610 } 17611 } 17612 } while ((current = next)); 17613 17614 // `break traverseScopesLoop;` takes us to here 17615 17616 if((dirty || asyncQueue.length) && !(ttl--)) { 17617 clearPhase(); 17618 throw $rootScopeMinErr('infdig', 17619 '{0} $digest() iterations reached. Aborting!\n' + 17620 'Watchers fired in the last 5 iterations: {1}', 17621 TTL, toJson(watchLog)); 17622 } 17623 17624 } while (dirty || asyncQueue.length); 17625 17626 clearPhase(); 17627 17628 while(postDigestQueue.length) { 17629 try { 17630 postDigestQueue.shift()(); 17631 } catch (e) { 17632 $exceptionHandler(e); 17633 } 17634 } 17635 }, 17636 17637 17638 /** 17639 * @ngdoc event 17640 * @name $rootScope.Scope#$destroy 17641 * @eventType broadcast on scope being destroyed 17642 * 17643 * @description 17644 * Broadcasted when a scope and its children are being destroyed. 17645 * 17646 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to 17647 * clean up DOM bindings before an element is removed from the DOM. 17648 */ 17649 17650 /** 17651 * @ngdoc method 17652 * @name $rootScope.Scope#$destroy 17653 * @kind function 17654 *
17655 * @description 17656 * Removes the current scope (and all of its children) from the parent scope. Removal implies 17657 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer 17658 * propagate to the current scope and its children. Removal also implies that the current 17659 * scope is eligible for garbage collection. 17660 * 17661 * The `$destroy()` is usually used by directives such as 17662 * {@link ng.directive:ngRepeat ngRepeat} for managing the 17663 * unrolling of the loop. 17664 * 17665 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. 17666 * Application code can register a `$destroy` event handler that will give it a chance to 17667 * perform any necessary cleanup. 17668 * 17669 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to 17670 * clean up DOM bindings before an element is removed from the DOM. 17671 */ 17672 $destroy: function() { 17673 // we can't destroy the root scope or a scope that has been already destroyed 17674 if (this.$$destroyed) return; 17675 var parent = this.$parent; 17676 17677 this.$broadcast('$destroy'); 17678 this.$$destroyed = true; 17679 if (this === $rootScope) return; 17680 17681 forEach(this.$$listenerCount, bind(null, decrementListenerCount, this)); 17682 17683 // sever all the references to parent scopes (after this cleanup, the current scope should 17684 // not be retained by any of our references and should be eligible for garbage collection) 17685 if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; 17686 if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; 17687 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; 17688 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; 17689 17690 17691 // All of the code below is bogus code that works around V8's memory leak via optimized code 17692 // and inline caches. 17693 // 17694 // see: 17695 // - https://code.google.com/p/v8/issues/detail?id=2073#c26 17696 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 17697 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 17698 17699 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = 17700 this.$$childTail = this.$root = null; 17701 17702 // don't reset these to null in case some async task tries to register a listener/watch/task 17703 this.$$listeners = {}; 17704 this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = []; 17705 17706 // prevent NPEs since these methods have references to properties we nulled out 17707 this.$destroy = this.$digest = this.$apply = noop; 17708 this.$on = this.$watch = function() { return noop; }; 17709 }, 17710 17711 /** 17712 * @ngdoc method 17713 * @name $rootScope.Scope#$eval 17714 * @kind function 17715 * 17716 * @description 17717 * Executes the `expression` on the current scope and returns the result. Any exceptions in 17718 * the expression are propagated (uncaught). This is useful when evaluating Angular 17719 * expressions. 17720 * 17721 * # Example 17722 * ```js 17723 var scope = ng.$rootScope.Scope(); 17724 scope.a = 1; 17725 scope.b = 2; 17726 17727 expect(scope.$eval('a+b')).toEqual(3); 17728 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); 17729 * ``` 17730 * 17731 * @param {(string|function())=} expression An angular expression to be executed. 17732 * 17733 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 17734 * - `function(scope)`: execute the function with the current `scope` parameter. 17735 * 17736 * @param {(object)=} locals Local variables object, useful for overriding values in scope. 17737 * @returns {*} The result of evaluating the expression. 17738 */ 17739 $eval: function(expr, locals) { 17740 return $parse(expr)(this, locals); 17741 }, 17742 17743 /** 17744 * @ngdoc method 17745 * @name $rootScope.Scope#$evalAsync 17746 * @kind function 17747 *
17748 * @description 17749 * Executes the expression on the current scope at a later point in time. 17750 * 17751 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only 17752 * that: 17753 * 17754 * - it will execute after the function that scheduled the evaluation (preferably before DOM 17755 * rendering). 17756 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after 17757 * `expression` execution. 17758 * 17759 * Any exceptions from the execution of the expression are forwarded to the 17760 * {@link ng.$exceptionHandler $exceptionHandler} service. 17761 * 17762 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle 17763 * will be scheduled. However, it is encouraged to always call code that changes the model 17764 * from within an `$apply` call. That includes code evaluated via `$evalAsync`. 17765 * 17766 * @param {(string|function())=} expression An angular expression to be executed. 17767 * 17768 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 17769 * - `function(scope)`: execute the function with the current `scope` parameter. 17770 * 17771 */ 17772 $evalAsync: function(expr) { 17773 // if we are outside of an $digest loop and this is the first time we are scheduling async 17774 // task also schedule async auto-flush 17775 if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { 17776 $browser.defer(function() { 17777 if ($rootScope.$$asyncQueue.length) { 17778 $rootScope.$digest(); 17779 } 17780 }); 17781 } 17782 17783 this.$$asyncQueue.push({scope: this, expression: expr}); 17784 }, 17785 17786 $$postDigest : function(fn) { 17787 this.$$postDigestQueue.push(fn); 17788 }, 17789 17790 /** 17791 * @ngdoc method 17792 * @name $rootScope.Scope#$apply 17793 * @kind function 17794 * 17795 * @description 17796 * `$apply()` is used to execute an expression in angular from outside of the angular 17797 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). 17798 * Because we are calling into the angular framework we need to perform proper scope life 17799 * cycle of {@link ng.$exceptionHandler exception handling}, 17800 * {@link ng.$rootScope.Scope#$digest executing watches}. 17801 * 17802 * ## Life cycle 17803 * 17804 * # Pseudo-Code of `$apply()` 17805 * ```js 17806 function $apply(expr) { 17807 try { 17808 return $eval(expr); 17809 } catch (e) { 17810 $exceptionHandler(e); 17811 } finally { 17812 $root.$digest(); 17813 } 17814 } 17815 * ``` 17816 * 17817 * 17818 * Scope's `$apply()` method transitions through the following stages: 17819 * 17820 * 1. The {@link guide/expression expression} is executed using the 17821 * {@link ng.$rootScope.Scope#$eval $eval()} method. 17822 * 2. Any exceptions from the execution of the expression are forwarded to the 17823 * {@link ng.$exceptionHandler $exceptionHandler} service. 17824 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the 17825 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. 17826 * 17827 * 17828 * @param {(string|function())=} exp An angular expression to be executed. 17829 * 17830 * - `string`: execute using the rules as defined in {@link guide/expression expression}. 17831 * - `function(scope)`: execute the function with current `scope` parameter. 17832 * 17833 * @returns {*} The result of evaluating the expression. 17834 */ 17835 $apply: function(expr) { 17836 try { 17837 beginPhase('$apply'); 17838 return this.$eval(expr); 17839 } catch (e) { 17840 $exceptionHandler(e); 17841 } finally { 17842 clearPhase(); 17843 try { 17844 $rootScope.$digest(); 17845 } catch (e) { 17846 $exceptionHandler(e); 17847 throw e; 17848 } 17849 } 17850 }, 17851 17852 /** 17853 * @ngdoc method 17854 * @name $rootScope.Scope#$on 17855 * @kind function 17856 *
17857 * @description 17858 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for 17859 * discussion of event life cycle. 17860 * 17861 * The event listener function format is: `function(event, args...)`. The `event` object 17862 * passed into the listener has the following attributes: 17863 * 17864 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or 17865 * `$broadcast`-ed. 17866 * - `currentScope` - `{Scope}`: the current scope which is handling the event. 17867 * - `name` - `{string}`: name of the event. 17868 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel 17869 * further event propagation (available only for events that were `$emit`-ed). 17870 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag 17871 * to true. 17872 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. 17873 * 17874 * @param {string} name Event name to listen on. 17875 * @param {function(event, ...args)} listener Function to call when the event is emitted. 17876 * @returns {function()} Returns a deregistration function for this listener. 17877 */ 17878 $on: function(name, listener) { 17879 var namedListeners = this.$$listeners[name]; 17880 if (!namedListeners) { 17881 this.$$listeners[name] = namedListeners = []; 17882 } 17883 namedListeners.push(listener); 17884 17885 var current = this; 17886 do { 17887 if (!current.$$listenerCount[name]) { 17888 current.$$listenerCount[name] = 0; 17889 } 17890 current.$$listenerCount[name]++; 17891 } while ((current = current.$parent)); 17892 17893 var self = this; 17894 return function() { 17895 namedListeners[indexOf(namedListeners, listener)] = null; 17896 decrementListenerCount(self, 1, name); 17897 }; 17898 }, 17899 17900 17901 /** 17902 * @ngdoc method 17903 * @name $rootScope.Scope#$emit 17904 * @kind function 17905 * 17906 * @description 17907 * Dispatches an event `name` upwards through the scope hierarchy notifying the 17908 * registered {@link ng.$rootScope.Scope#$on} listeners. 17909 * 17910 * The event life cycle starts at the scope on which `$emit` was called. All 17911 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get 17912 * notified. Afterwards, the event traverses upwards toward the root scope and calls all 17913 * registered listeners along the way. The event will stop propagating if one of the listeners 17914 * cancels it. 17915 * 17916 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed 17917 * onto the {@link ng.$exceptionHandler $exceptionHandler} service. 17918 * 17919 * @param {string} name Event name to emit. 17920 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. 17921 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). 17922 */ 17923 $emit: function(name, args) { 17924 var empty = [], 17925 namedListeners, 17926 scope = this, 17927 stopPropagation = false, 17928 event = { 17929 name: name, 17930 targetScope: scope, 17931 stopPropagation: function() {stopPropagation = true;}, 17932 preventDefault: function() { 17933 event.defaultPrevented = true; 17934 }, 17935 defaultPrevented: false 17936 }, 17937 listenerArgs = concat([event], arguments, 1), 17938 i, length; 17939 17940 do { 17941 namedListeners = scope.$$listeners[name] || empty; 17942 event.currentScope = scope; 17943 for (i=0, length=namedListeners.length; i<length; i++) { 17944 17945 // if listeners were deregistered, defragment the array 17946 if (!namedListeners[i]) { 17947 namedListeners.splice(i, 1); 17948 i--; 17949 length--; 17950 continue; 17951 } 17952 try { 17953 //allow all listeners attached to the current scope to run 17954 namedListeners[i].apply(null, listenerArgs); 17955 } catch (e) { 17956 $exceptionHandler(e); 17957 } 17958 } 17959 //if any listener on the current scope stops propagation, prevent bubbling 17960 if (stopPropagation) return event; 17961 //traverse upwards 17962 scope = scope.$parent; 17963 } while (scope); 17964 17965 return event; 17966 }, 17967 17968 17969 /** 17970 * @ngdoc method 17971 * @name $rootScope.Scope#$broadcast 17972 * @kind function 17973 *
17974 * @description 17975 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the 17976 * registered {@link ng.$rootScope.Scope#$on} listeners. 17977 * 17978 * The event life cycle starts at the scope on which `$broadcast` was called. All 17979 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get 17980 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current 17981 * scope and calls all registered listeners along the way. The event cannot be canceled. 17982 * 17983 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed 17984 * onto the {@link ng.$exceptionHandler $exceptionHandler} service. 17985 * 17986 * @param {string} name Event name to broadcast. 17987 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. 17988 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} 17989 */ 17990 $broadcast: function(name, args) { 17991 var target = this, 17992 current = target, 17993 next = target, 17994 event = { 17995 name: name, 17996 targetScope: target, 17997 preventDefault: function() { 17998 event.defaultPrevented = true; 17999 }, 18000 defaultPrevented: false 18001 }, 18002 listenerArgs = concat([event], arguments, 1), 18003 listeners, i, length; 18004 18005 //down while you can, then up and next sibling or up and next sibling until back at root 18006 while ((current = next)) { 18007 event.currentScope = current; 18008 listeners = current.$$listeners[name] || []; 18009 for (i=0, length = listeners.length; i<length; i++) { 18010 // if listeners were deregistered, defragment the array 18011 if (!listeners[i]) { 18012 listeners.splice(i, 1); 18013 i--; 18014 length--; 18015 continue; 18016 } 18017 18018 try { 18019 listeners[i].apply(null, listenerArgs); 18020 } catch(e) { 18021 $exceptionHandler(e); 18022 } 18023 } 18024 18025 // Insanity Warning: scope depth-first traversal 18026 // yes, this code is a bit crazy, but it works and we have tests to prove it! 18027 // this piece should be kept in sync with the traversal in $digest 18028 // (though it differs due to having the extra check for $$listenerCount) 18029 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || 18030 (current !== target && current.$$nextSibling)))) { 18031 while(current !== target && !(next = current.$$nextSibling)) { 18032 current = current.$parent; 18033 } 18034 } 18035 } 18036 18037 return event; 18038 } 18039 }; 18040 18041 var $rootScope = new Scope(); 18042 18043 return $rootScope; 18044 18045 18046 function beginPhase(phase) { 18047 if ($rootScope.$$phase) { 18048 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); 18049 } 18050 18051 $rootScope.$$phase = phase; 18052 } 18053 18054 function clearPhase() { 18055 $rootScope.$$phase = null; 18056 } 18057 18058 function compileToFn(exp, name) { 18059 var fn = $parse(exp); 18060 assertArgFn(fn, name); 18061 return fn; 18062 } 18063 18064 function decrementListenerCount(current, count, name) { 18065 do { 18066 current.$$listenerCount[name] -= count; 18067 18068 if (current.$$listenerCount[name] === 0) { 18069 delete current.$$listenerCount[name]; 18070 } 18071 } while ((current = current.$parent)); 18072 } 18073 18074 /** 18075 * function used as an initial value for watchers. 18076 * because it's unique we can easily tell it apart from other values 18077 */ 18078 function initWatchVal() {} 18079 }]; 18080} 18081 18082/** 18083 * @description 18084 * Private service to sanitize uris for links and images. Used by $compile and $sanitize. 18085 */ 18086function $$SanitizeUriProvider() { 18087 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, 18088 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file):|data:image\/)/; 18089 18090 /** 18091 * @description 18092 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 18093 * urls during a[href] sanitization. 18094 * 18095 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 18096 * 18097 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into 18098 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` 18099 * regular expression. If a match is found, the original url is
18099written into the dom. Otherwise, 18100 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 18101 * 18102 * @param {RegExp=} regexp New regexp to whitelist urls with. 18103 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 18104 * chaining otherwise. 18105 */ 18106 this.aHrefSanitizationWhitelist = function(regexp) { 18107 if (isDefined(regexp)) { 18108 aHrefSanitizationWhitelist = regexp; 18109 return this; 18110 } 18111 return aHrefSanitizationWhitelist; 18112 }; 18113 18114 18115 /** 18116 * @description 18117 * Retrieves or overrides the default regular expression that is used for whitelisting of safe 18118 * urls during img[src] sanitization. 18119 * 18120 * The sanitization is a security measure aimed at prevent XSS attacks via html links. 18121 * 18122 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into 18123 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` 18124 * regular expression. If a match is found, the original url is written into the dom. Otherwise, 18125 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. 18126 * 18127 * @param {RegExp=} regexp New regexp to whitelist urls with. 18128 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for 18129 * chaining otherwise. 18130 */ 18131 this.imgSrcSanitizationWhitelist = function(regexp) { 18132 if (isDefined(regexp)) { 18133 imgSrcSanitizationWhitelist = regexp; 18134 return this; 18135 } 18136 return imgSrcSanitizationWhitelist; 18137 }; 18138 18139 this.$get = function() { 18140 return function sanitizeUri(uri, isImage) { 18141 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; 18142 var normalizedVal; 18143 // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case. 18144 if (!msie || msie >= 8 ) { 18145 normalizedVal = urlResolve(uri).href; 18146 if (normalizedVal !== '' && !normalizedVal.match(regex)) { 18147 return 'unsafe:'+normalizedVal; 18148 } 18149 } 18150 return uri; 18151 }; 18152 }; 18153} 18154 18155var $sceMinErr = minErr('$sce'); 18156 18157var SCE_CONTEXTS = { 18158 HTML: 'html', 18159 CSS: 'css', 18160 URL: 'url', 18161 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a 18162 // url. (e.g. ng-include, script src, templateUrl) 18163 RESOURCE_URL: 'resourceUrl', 18164 JS: 'js' 18165}; 18166 18167// Helper functions follow. 18168 18169// Copied from: 18170// http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962 18171// Prereq: s is a string. 18172function escapeForRegexp(s) { 18173 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). 18174 replace(/\x08/g, '\\x08'); 18175} 18176 18177 18178function adjustMatcher(matcher) { 18179 if (matcher === 'self') { 18180 return matcher; 18181 } else if (isString(matcher)) { 18182 // Strings match exactly except for 2 wildcards - '*' and '**'. 18183 // '*' matches any character except those from the set ':/.?&'. 18184 // '**' matches any character (like .* in a RegExp). 18185 // More than 2 *'s raises an error as it's ill defined. 18186 if (matcher.indexOf('***') > -1) { 18187 throw $sceMinErr('iwcard', 18188 'Illegal sequence *** in string matcher. String: {0}', matcher); 18189 } 18190 matcher = escapeForRegexp(matcher). 18191 replace('\\*\\*', '.*'). 18192 replace('\\*', '[^:/.?&;]*'); 18193 return new RegExp('^' + matcher + '$'); 18194 } else if (isRegExp(matcher)) { 18195 // The only other type of matcher allowed is a Regexp. 18196 // Match entire URL / disallow partial matches. 18197 // Flags are reset (i.e. no global, ignoreCase or multiline) 18198 return new RegExp('^' + matcher.source + '$'); 18199 } else { 18200 throw $sceMinErr('imatcher', 18201 'Matchers may only be "self", string patterns or RegExp objects'); 18202 } 18203} 18204 18205 18206function adjustMatchers(matchers) { 18207 var adjustedMatchers = []; 18208 if (isDefined(matchers)) { 18209 forEach(matchers, function(matcher) { 18210 adjustedMatchers.push(adjustMatcher(matcher)); 18211 }); 18212 } 18213 return adjustedMatchers; 18214} 18215 18216 18217/** 18218 * @ngdoc service 18219 * @name $sceDelegate 18220 * @kind function 18221 * 18222 * @description 18223 * 18224 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict 18225 * Contextual Escaping (SCE)} services to AngularJS. 18226 * 18227 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of 18228 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is 18229 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to 18230 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things 18231 * work because `$sce` delegates to `$sceDelegate` for these operations. 18232 * 18233 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. 18234 * 18235 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
18236 * can override it completely to change the behavior of `$sce`, the common case would 18237 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting 18238 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as 18239 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist 18240 * $sceDelegateProvider.resourceUrlWhitelist} and {@link 18241 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} 18242 */ 18243 18244/** 18245 * @ngdoc provider 18246 * @name $sceDelegateProvider 18247 * @description 18248 * 18249 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate 18250 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure 18251 * that the URLs used for sourcing Angular templates are safe. Refer {@link 18252 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and 18253 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} 18254 * 18255 * For the general details about this service in Angular, read the main page for {@link ng.$sce 18256 * Strict Contextual Escaping (SCE)}. 18257 * 18258 * **Example**: Consider the following case. <a name="example"></a> 18259 * 18260 * - your app is hosted at url `http://myapp.example.com/` 18261 * - but some of your templates are hosted on other domains you control such as 18262 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc. 18263 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. 18264 * 18265 * Here is what a secure configuration for this scenario might look like: 18266 * 18267 * ``` 18268 * angular.module('myApp', []).config(function($sceDelegateProvider) { 18269 * $sceDelegateProvider.resourceUrlWhitelist([ 18270 * // Allow same origin resource loads. 18271 * 'self', 18272 * // Allow loading from our assets domain. Notice the difference between * and **. 18273 * 'http://srv*.assets.example.com/**' 18274 * ]); 18275 * 18276 * // The blacklist overrides the whitelist so the open redirect here is blocked. 18277 * $sceDelegateProvider.resourceUrlBlacklist([ 18278 * 'http://myapp.example.com/clickThru**' 18279 * ]); 18280 * }); 18281 * ``` 18282 */ 18283 18284function $SceDelegateProvider() { 18285 this.SCE_CONTEXTS = SCE_CONTEXTS; 18286 18287 // Resource URLs can also be trusted by policy. 18288 var resourceUrlWhitelist = ['self'], 18289 resourceUrlBlacklist = []; 18290 18291 /** 18292 * @ngdoc method 18293 * @name $sceDelegateProvider#resourceUrlWhitelist 18294 * @kind function 18295 * 18296 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value 18297 * provided. This must be an array or null. A snapshot of this array is used so further 18298 * changes to the array are ignored. 18299 * 18300 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items 18301 * allowed in this array. 18302 * 18303 * Note: **an empty whitelist array will block all URLs**! 18304 * 18305 * @return {Array} the currently set whitelist array. 18306 * 18307 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only 18308 * same origin resource requests. 18309 * 18310 * @description 18311 * Sets/Gets the whitelist of trusted resource URLs. 18312 */ 18313 this.resourceUrlWhitelist = function (value) { 18314 if (arguments.length) { 18315 resourceUrlWhitelist = adjustMatchers(value); 18316 } 18317 return resourceUrlWhitelist; 18318 }; 18319 18320 /** 18321 * @ngdoc method 18322 * @name $sceDelegateProvider#resourceUrlBlacklist 18323 * @kind function 18324 * 18325 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value 18326 * provided. This must be an array or null. A snapshot of this array is used so further 18327 * changes to the array are ignored. 18328 * 18329 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items 18330 * allowed in this array. 18331 * 18332 * The typical usage for the blacklist is to **block 18333 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as 18334 * these would otherwise be trusted but actually return content from the redirected domain. 18335 *
18336 * Finally, **the blacklist overrides the whitelist** and has the final say. 18337 * 18338 * @return {Array} the currently set blacklist array. 18339 * 18340 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there 18341 * is no blacklist.) 18342 * 18343 * @description 18344 * Sets/Gets the blacklist of trusted resource URLs. 18345 */ 18346 18347 this.resourceUrlBlacklist = function (value) { 18348 if (arguments.length) { 18349 resourceUrlBlacklist = adjustMatchers(value); 18350 } 18351 return resourceUrlBlacklist; 18352 }; 18353 18354 this.$get = ['$injector', function($injector) { 18355 18356 var htmlSanitizer = function htmlSanitizer(html) { 18357 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); 18358 }; 18359 18360 if ($injector.has('$sanitize')) { 18361 htmlSanitizer = $injector.get('$sanitize'); 18362 } 18363 18364 18365 function matchUrl(matcher, parsedUrl) { 18366 if (matcher === 'self') { 18367 return urlIsSameOrigin(parsedUrl); 18368 } else { 18369 // definitely a regex. See adjustMatchers() 18370 return !!matcher.exec(parsedUrl.href); 18371 } 18372 } 18373 18374 function isResourceUrlAllowedByPolicy(url) { 18375 var parsedUrl = urlResolve(url.toString()); 18376 var i, n, allowed = false; 18377 // Ensure that at least one item from the whitelist allows this url. 18378 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { 18379 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { 18380 allowed = true; 18381 break; 18382 } 18383 } 18384 if (allowed) { 18385 // Ensure that no item from the blacklist blocked this url. 18386 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { 18387 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { 18388 allowed = false; 18389 break; 18390 } 18391 } 18392 } 18393 return allowed; 18394 } 18395 18396 function generateHolderType(Base) { 18397 var holderType = function TrustedValueHolderType(trustedValue) { 18398 this.$$unwrapTrustedValue = function() { 18399 return trustedValue; 18400 }; 18401 }; 18402 if (Base) { 18403 holderType.prototype = new Base(); 18404 } 18405 holderType.prototype.valueOf = function sceValueOf() { 18406 return this.$$unwrapTrustedValue(); 18407 }; 18408 holderType.prototype.toString = function sceToString() { 18409 return this.$$unwrapTrustedValue().toString(); 18410 }; 18411 return holderType; 18412 } 18413 18414 var trustedValueHolderBase = generateHolderType(), 18415 byType = {}; 18416 18417 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); 18418 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); 18419 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); 18420 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); 18421 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); 18422 18423 /** 18424 * @ngdoc method 18425 * @name $sceDelegate#trustAs 18426 * 18427 * @description 18428 * Returns an object that is trusted by angular for use in specified strict 18429 * contextual escaping contexts (such as ng-bind-html, ng-include, any src 18430 * attribute interpolation, any dom event binding attribute interpolation 18431 * such as for onclick, etc.) that uses the provided value. 18432 * See {@link ng.$sce $sce} for enabling strict contextual escaping. 18433 * 18434 * @param {string} type The kind of context in which this value is safe for use. e.g. url, 18435 * resourceUrl, html, js and css. 18436 * @param {*} value The value that that should be considered trusted/safe. 18437 * @returns {*} A value that can be used to stand in for the provided `value` in places 18438 * where Angular expects a $sce.trustAs() return value. 18439 */ 18440 function trustAs(type, trustedValue) { 18441 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); 18442 if (!Constructor) { 18443 throw $sceMinErr('icontext', 18444 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', 18445 type, trustedValue); 18446 } 18447 if (trustedValue === null || trustedValue === undefined || trustedValue === '') { 18448 return trustedValue; 18449 } 18450 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting 18451 // mutable objects, we ensure here that the value passed in is actually a string. 18452 if (typeof trustedValue !== 'string') { 18453 throw $sceMinErr('itype', 18454 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', 18455 type); 18456 } 18457 return new Constructor(trustedValue); 18458 } 18459 18460 /** 18461 * @ngdoc method 18462 * @name $sceDelegate#valueOf 18463 *
18464 * @description 18465 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs 18466 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link 18467 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. 18468 * 18469 * If the passed parameter is not a value that had been returned by {@link 18470 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. 18471 * 18472 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} 18473 * call or anything else. 18474 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs 18475 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns 18476 * `value` unchanged. 18477 */ 18478 function valueOf(maybeTrusted) { 18479 if (maybeTrusted instanceof trustedValueHolderBase) { 18480 return maybeTrusted.$$unwrapTrustedValue(); 18481 } else { 18482 return maybeTrusted; 18483 } 18484 } 18485 18486 /** 18487 * @ngdoc method 18488 * @name $sceDelegate#getTrusted 18489 * 18490 * @description 18491 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and 18492 * returns the originally supplied value if the queried context type is a supertype of the 18493 * created type. If this condition isn't satisfied, throws an exception. 18494 * 18495 * @param {string} type The kind of context in which this value is to be used. 18496 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs 18497 * `$sceDelegate.trustAs`} call. 18498 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs 18499 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. 18500 */ 18501 function getTrusted(type, maybeTrusted) { 18502 if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { 18503 return maybeTrusted; 18504 } 18505 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); 18506 if (constructor && maybeTrusted instanceof constructor) { 18507 return maybeTrusted.$$unwrapTrustedValue(); 18508 } 18509 // If we get here, then we may only take one of two actions. 18510 // 1. sanitize the value for the requested type, or 18511 // 2. throw an exception. 18512 if (type === SCE_CONTEXTS.RESOURCE_URL) { 18513 if (isResourceUrlAllowedByPolicy(maybeTrusted)) { 18514 return maybeTrusted; 18515 } else { 18516 throw $sceMinErr('insecurl', 18517 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', 18518 maybeTrusted.toString()); 18519 } 18520 } else if (type === SCE_CONTEXTS.HTML) { 18521 return htmlSanitizer(maybeTrusted); 18522 } 18523 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); 18524 } 18525 18526 return { trustAs: trustAs, 18527 getTrusted: getTrusted, 18528 valueOf: valueOf }; 18529 }]; 18530} 18531 18532 18533/** 18534 * @ngdoc provider 18535 * @name $sceProvider 18536 * @description 18537 * 18538 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. 18539 * - enable/disable Strict Contextual Escaping (SCE) in a module 18540 * - override the default implementation with a custom delegate 18541 * 18542 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. 18543 */ 18544 18545/* jshint maxlen: false*/ 18546 18547/** 18548 * @ngdoc service 18549 * @name $sce 18550 * @kind function 18551 * 18552 * @description 18553 * 18554 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. 18555 * 18556 * # Strict Contextual Escaping 18557 * 18558 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain 18559 * contexts to result in a value that is marked as safe to use for that context. One example of 18560 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer 18561 * to these contexts as privileged or SCE contexts. 18562 * 18563 * As of version 1.2, Angular ships with SCE enabled by default. 18564 * 18565 * Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows 18566 * one to execute arbitrary javascript by the use of the expression() syntax. Refer 18567 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
18568 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` 18569 * to the top of your HTML document. 18570 * 18571 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for 18572 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. 18573 * 18574 * Here's an example of a binding in a privileged context: 18575 * 18576 * ``` 18577 * <input ng-model="userHtml"> 18578 * <div ng-bind-html="userHtml"></div> 18579 * ``` 18580 * 18581 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE 18582 * disabled, this application allows the user to render arbitrary HTML into the DIV. 18583 * In a more realistic example, one may be rendering user comments, blog articles, etc. via 18584 * bindings. (HTML is just one example of a context where rendering user controlled input creates 18585 * security vulnerabilities.) 18586 * 18587 * For the case of HTML, you might use a library, either on the client side, or on the server side, 18588 * to sanitize unsafe HTML before binding to the value and rendering it in the document. 18589 * 18590 * How would you ensure that every place that used these types of bindings was bound to a value that 18591 * was sanitized by your library (or returned as safe for rendering by your server?) How can you 18592 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some 18593 * properties/fields and forgot to update the binding to the sanitized value? 18594 * 18595 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can 18596 * determine that something explicitly says it's safe to use a value for binding in that
18597 * context. You can then audit your code (a simple grep would do) to ensure that this is only done 18598 * for those values that you can easily tell are safe - because they were received from your server, 18599 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps 18600 * allowing only the files in a specific directory to do this. Ensuring that the internal API 18601 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. 18602 * 18603 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} 18604 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to 18605 * obtain values that will be accepted by SCE / privileged contexts. 18606 * 18607 * 18608 * ## How does it work? 18609 * 18610 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted 18611 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link 18612 * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the 18613 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. 18614 * 18615 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link 18616 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly 18617 * simplified): 18618 * 18619 * ``` 18620 * var ngBindHtmlDirective = ['$sce', function($sce) { 18621 * return function(scope, element, attr) { 18622 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { 18623 * element.html(value || ''); 18624 * }); 18625 * }; 18626 * }]; 18627 * ``` 18628 * 18629 * ## Impact on loading templates 18630 * 18631 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as 18632 * `templateUrl`'s specified by {@link guide/directive directives}. 18633 * 18634 * By default, Angular only loads templates from the same domain and protocol as the application 18635 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl 18636 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or 18637 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist 18638 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. 18639 * 18640 * *Please note*: 18641 * The browser's 18642 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) 18643 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) 18644 * policy apply in addition to this and may further restrict whether the template is successfully 18645 * loaded. This means that without the right CORS policy, loading templates from a different domain 18646 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some 18647 * browsers. 18648 * 18649 * ## This feels like too much overhead for the developer? 18650 * 18651 * It's important to remember that SCE only applies to interpolation expressions. 18652 * 18653 * If your expressions are constant literals, they're automatically trusted and you don't need to 18654 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. 18655 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. 18656 * 18657 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them 18658 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. 18659 * 18660 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load 18661 * templates in `ng-include` from your application's domain without having to even know about SCE. 18662 * It blocks loading templates from other domains or loading templates over http from an https 18663 * served document. You can change these by setting your own custom {@link 18664 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link 18665 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. 18666 * 18667 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an 18668 * application that's secure and can be audited to verify that with much more ease than bolting 18669 * security onto an application later. 18670 * 18671 * <a name="contexts"></a> 18672 * ## What trusted context types are supported? 18673 * 18674 * | Context | Notes | 18675 * |---------------------|----------------| 18676 * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | 18677 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | 18678 * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. | 18679 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | 18680 * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | 18681 * 18682 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> 18683 * 18684 * Each element in these arrays must be one of the following: 18685 * 18686 * - **'self'** 18687 * - The special **string**, `'self'`, can be used to match against all URLs of the **same 18688 * domain** as the application document using the **same protocol**. 18689 * - **String** (except the special value `'self'`) 18690 * - The string is matched against the full *normalized / absolute URL* of the resource 18691 * being tested (substring matches are not good enough.) 18692 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters 18693 * match themselves. 18694 * - `*`: matches zero or more occurrences of any character other than one of the following 6 18695 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
18696 * in a whitelist. 18697 * - `**`: matches zero or more occurrences of *any* character. As such, it's not 18698 * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g. 18699 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might 18700 * not have been the intention.) Its usage at the very end of the path is ok. (e.g. 18701 * http://foo.example.com/templates/**). 18702 * - **RegExp** (*see caveat below*) 18703 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax 18704 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to 18705 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should 18706 * have good test coverage.). For instance, the use of `.` in the regex is correct only in a 18707 * small number of cases. A `.` character in the regex used when matching the scheme or a 18708 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It 18709 * is highly recommended to use the string patterns and only fall back to regular expressions 18710 * if they as a last resort. 18711 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is 18712 * matched against the **entire** *normalized / absolute URL* of the resource being tested 18713 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags 18714 * present on the RegExp (such as multiline, global, ignoreCase) are ignored. 18715 * - If you are generating your JavaScript from some other templating engine (not 18716 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), 18717 * remember to escape your regular expression (and be aware that you might need more than 18718 * one level of escaping depending on your templating engine and the way you interpolated 18719 * the value.) Do make use of your platform's escaping mechanism as it might be good 18720 * enough before coding your own. e.g. Ruby has 18721 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) 18722 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). 18723 * Javascript lacks a similar built in function for escaping. Take a look at Google 18724 * Closure library's [goog.string.regExpEscape(s)]( 18725 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). 18726 * 18727 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. 18728 * 18729 * ## Show me an example using SCE. 18730 * 18731 * <example module="mySceApp" deps="angular-sanitize.js"> 18732 * <file name="index.html"> 18733 * <div ng-controller="myAppController as myCtrl"> 18734 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> 18735 * <b>User comments</b><br> 18736 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when 18737 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an 18738 * exploit. 18739 * <div class="well"> 18740 * <div ng-repeat="userComment in myCtrl.userComments"> 18741 * <b>{{userComment.name}}</b>: 18742 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> 18743 * <br> 18744 * </div> 18745 * </div> 18746 * </div> 18747 * </file> 18748 * 18749 * <file name="script.js"> 18750 * var mySceApp = angular.module('mySceApp', ['ngSanitize']); 18751 * 18752 * mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) { 18753 * var self = this; 18754 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { 18755 * self.userComments = userComments; 18756 * }); 18757 * self.explicitlyTrustedHtml = $sce.trustAsHtml( 18758 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + 18759 * 'sanitization."">Hover over this text.</span>'); 18760 * }); 18761 * </file> 18762 * 18763 * <file name="test_data.json"> 18764 * [ 18765 * { "name": "Alice", 18766 * "htmlComment": 18767 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" 18768 * }, 18769 * { "name": "Bob",
18770 * "htmlComment": "<i>Yes!</i> Am I the only other one?" 18771 * } 18772 * ] 18773 * </file> 18774 * 18775 * <file name="protractor.js" type="protractor"> 18776 * describe('SCE doc demo', function() { 18777 * it('should sanitize untrusted values', function() { 18778 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) 18779 * .toBe('<span>Is <i>anyone</i> reading this?</span>'); 18780 * }); 18781 * 18782 * it('should NOT sanitize explicitly trusted values', function() { 18783 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( 18784 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + 18785 * 'sanitization."">Hover over this text.</span>'); 18786 * }); 18787 * }); 18788 * </file> 18789 * </example> 18790 * 18791 * 18792 * 18793 * ## Can I disable SCE completely? 18794 * 18795 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits 18796 * for little coding overhead. It will be much harder to take an SCE disabled application and 18797 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE 18798 * for cases where you have a lot of existing code that was written before SCE was introduced and 18799 * you're migrating them a module at a time. 18800 * 18801 * That said, here's how you can completely disable SCE: 18802 * 18803 * ``` 18804 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { 18805 * // Completely disable SCE. For demonstration purposes only! 18806 * // Do not use in new projects. 18807 * $sceProvider.enabled(false); 18808 * }); 18809 * ``` 18810 * 18811 */ 18812/* jshint maxlen: 100 */ 18813 18814function $SceProvider() { 18815 var enabled = true; 18816 18817 /** 18818 * @ngdoc method 18819 * @name $sceProvider#enabled 18820 * @kind function 18821 * 18822 * @param {boolean=} value If provided, then enables/disables SCE. 18823 * @return {boolean} true if SCE is enabled, false otherwise. 18824 * 18825 * @description 18826 * Enables/disables SCE and returns the current value. 18827 */ 18828 this.enabled = function (value) { 18829 if (arguments.length) { 18830 enabled = !!value; 18831 } 18832 return enabled; 18833 }; 18834 18835 18836 /* Design notes on the default implementation for SCE. 18837 * 18838 * The API contract for the SCE delegate 18839 * ------------------------------------- 18840 * The SCE delegate object must provide the following 3 methods: 18841 * 18842 * - trustAs(contextEnum, value) 18843 * This method is used to tell the SCE service that the provided value is OK to use in the 18844 * contexts specified by contextEnum. It must return an object that will be accepted by 18845 * getTrusted() for a compatible contextEnum and return this value. 18846 * 18847 * - valueOf(value) 18848 * For values that were not produced by trustAs(), return them as is. For values that were 18849 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if 18850 * trustAs is wrapping the given values into some type, this operation unwraps it when given 18851 * such a value. 18852 * 18853 * - getTrusted(contextEnum, value) 18854 * This function should return the a value that is safe to use in the context specified by 18855 * contextEnum or throw and exception otherwise. 18856 * 18857 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be 18858 * opaque or wrapped in some holder object. That happens to be an implementation detail. For 18859 * instance, an implementation could maintain a registry of all trusted objects by context. In 18860 * such a case, trustAs() would return the same object that was passed in. getTrusted() would 18861 * return the same object passed in if it was found in the registry under a compatible context or 18862 * throw an exception otherwise. An implementation might only wrap values some of the time based 18863 * on some criteria. getTrusted() might return a value and not throw an exception for special 18864 * constants or objects even if not wrapped. All such implementations fulfill this contract. 18865 * 18866 * 18867 * A note on the inheritance model for SCE contexts 18868 * ------------------------------------------------ 18869 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This 18870 * is purely an implementation details. 18871 * 18872 * The contract is simply this: 18873 * 18874 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) 18875 * will also succeed. 18876 * 18877 * Inheritance happens to capture this in a natural way. In some future, we
18878 * may not use inheritance anymore. That is OK because no code outside of 18879 * sce.js and sceSpecs.js would need to be aware of this detail. 18880 */ 18881 18882 this.$get = ['$parse', '$sniffer', '$sceDelegate', function( 18883 $parse, $sniffer, $sceDelegate) { 18884 // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows 18885 // the "expression(javascript expression)" syntax which is insecure. 18886 if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) { 18887 throw $sceMinErr('iequirks', 18888 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' + 18889 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + 18890 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); 18891 } 18892 18893 var sce = shallowCopy(SCE_CONTEXTS); 18894 18895 /** 18896 * @ngdoc method 18897 * @name $sce#isEnabled 18898 * @kind function 18899 * 18900 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you 18901 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. 18902 * 18903 * @description 18904 * Returns a boolean indicating if SCE is enabled. 18905 */ 18906 sce.isEnabled = function () { 18907 return enabled; 18908 }; 18909 sce.trustAs = $sceDelegate.trustAs; 18910 sce.getTrusted = $sceDelegate.getTrusted; 18911 sce.valueOf = $sceDelegate.valueOf; 18912 18913 if (!enabled) { 18914 sce.trustAs = sce.getTrusted = function(type, value) { return value; }; 18915 sce.valueOf = identity; 18916 } 18917 18918 /** 18919 * @ngdoc method 18920 * @name $sce#parseAs 18921 * 18922 * @description 18923 * Converts Angular {@link guide/expression expression} into a function. This is like {@link 18924 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it 18925 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, 18926 * *result*)} 18927 * 18928 * @param {string} type The kind of SCE context in which this result will be used. 18929 * @param {string} expression String expression to compile. 18930 * @returns {function(context, locals)} a function which represents the compiled expression: 18931 * 18932 * * `context` â `{object}` â an object against which any expressions embedded in the strings 18933 * are evaluated against (typically a scope object). 18934 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 18935 * `context`. 18936 */ 18937 sce.parseAs = function sceParseAs(type, expr) { 18938 var parsed = $parse(expr); 18939 if (parsed.literal && parsed.constant) { 18940 return parsed; 18941 } else { 18942 return function sceParseAsTrusted(self, locals) { 18943 return sce.getTrusted(type, parsed(self, locals)); 18944 }; 18945 } 18946 }; 18947 18948 /** 18949 * @ngdoc method 18950 * @name $sce#trustAs 18951 * 18952 * @description 18953 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, 18954 * returns an object that is trusted by angular for use in specified strict contextual 18955 * escaping contexts (such as ng-bind-html, ng-include, any src attribute 18956 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) 18957 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual 18958 * escaping. 18959 * 18960 * @param {string} type The kind of context in which this value is safe for use. e.g. url, 18961 * resource_url, html, js and css. 18962 * @param {*} value The value that that should be considered trusted/safe. 18963 * @returns {*} A value that can be used to stand in for the provided `value` in places 18964 * where Angular expects a $sce.trustAs() return value. 18965 */ 18966 18967 /** 18968 * @ngdoc method 18969 * @name $sce#trustAsHtml 18970 * 18971 * @description 18972 * Shorthand method. `$sce.trustAsHtml(value)` â 18973 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} 18974 * 18975 * @param {*} value The value to trustAs. 18976 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml 18977 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives 18978 * only accept expressions that are either literal constants or are the 18979 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 18980 */ 18981 18982 /** 18983 * @ngdoc method 18984 * @name $sce#trustAsUrl 18985 *
18986 * @description 18987 * Shorthand method. `$sce.trustAsUrl(value)` â 18988 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} 18989 * 18990 * @param {*} value The value to trustAs. 18991 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl 18992 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives 18993 * only accept expressions that are either literal constants or are the 18994 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 18995 */ 18996 18997 /** 18998 * @ngdoc method 18999 * @name $sce#trustAsResourceUrl 19000 * 19001 * @description 19002 * Shorthand method. `$sce.trustAsResourceUrl(value)` â 19003 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} 19004 * 19005 * @param {*} value The value to trustAs. 19006 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl 19007 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives 19008 * only accept expressions that are either literal constants or are the return 19009 * value of {@link ng.$sce#trustAs $sce.trustAs}.) 19010 */ 19011 19012 /** 19013 * @ngdoc method 19014 * @name $sce#trustAsJs 19015 * 19016 * @description 19017 * Shorthand method. `$sce.trustAsJs(value)` â 19018 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} 19019 * 19020 * @param {*} value The value to trustAs. 19021 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs 19022 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives 19023 * only accept expressions that are either literal constants or are the 19024 * return value of {@link ng.$sce#trustAs $sce.trustAs}.) 19025 */ 19026 19027 /** 19028 * @ngdoc method 19029 * @name $sce#getTrusted 19030 * 19031 * @description 19032 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, 19033 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the 19034 * originally supplied value if the queried context type is a supertype of the created type. 19035 * If this condition isn't satisfied, throws an exception. 19036 * 19037 * @param {string} type The kind of context in which this value is to be used. 19038 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} 19039 * call. 19040 * @returns {*} The value the was originally provided to 19041 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. 19042 * Otherwise, throws an exception. 19043 */ 19044 19045 /** 19046 * @ngdoc method 19047 * @name $sce#getTrustedHtml 19048 * 19049 * @description 19050 * Shorthand method. `$sce.getTrustedHtml(value)` â 19051 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} 19052 * 19053 * @param {*} value The value to pass to `$sce.getTrusted`. 19054 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` 19055 */ 19056 19057 /** 19058 * @ngdoc method 19059 * @name $sce#getTrustedCss 19060 * 19061 * @description 19062 * Shorthand method. `$sce.getTrustedCss(value)` â 19063 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} 19064 * 19065 * @param {*} value The value to pass to `$sce.getTrusted`. 19066 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` 19067 */ 19068 19069 /** 19070 * @ngdoc method 19071 * @name $sce#getTrustedUrl 19072 * 19073 * @description 19074 * Shorthand method. `$sce.getTrustedUrl(value)` â 19075 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} 19076 * 19077 * @param {*} value The value to pass to `$sce.getTrusted`. 19078 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` 19079 */ 19080 19081 /** 19082 * @ngdoc method 19083 * @name $sce#getTrustedResourceUrl 19084 * 19085 * @description 19086 * Shorthand method. `$sce.getTrustedResourceUrl(value)` â 19087 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} 19088 * 19089 * @param {*} value The value to pass to `$sceDelegate.getTrusted`. 19090 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` 19091 */ 19092 19093 /** 19094 * @ngdoc method 19095 * @name $sce#getTrustedJs 19096 *
19097 * @description 19098 * Shorthand method. `$sce.getTrustedJs(value)` â 19099 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} 19100 * 19101 * @param {*} value The value to pass to `$sce.getTrusted`. 19102 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` 19103 */ 19104 19105 /** 19106 * @ngdoc method 19107 * @name $sce#parseAsHtml 19108 * 19109 * @description 19110 * Shorthand method. `$sce.parseAsHtml(expression string)` â 19111 * {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`} 19112 * 19113 * @param {string} expression String expression to compile. 19114 * @returns {function(context, locals)} a function which represents the compiled expression: 19115 * 19116 * * `context` â `{object}` â an object against which any expressions embedded in the strings 19117 * are evaluated against (typically a scope object). 19118 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 19119 * `context`. 19120 */ 19121 19122 /** 19123 * @ngdoc method 19124 * @name $sce#parseAsCss 19125 * 19126 * @description 19127 * Shorthand method. `$sce.parseAsCss(value)` â 19128 * {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`} 19129 * 19130 * @param {string} expression String expression to compile. 19131 * @returns {function(context, locals)} a function which represents the compiled expression: 19132 * 19133 * * `context` â `{object}` â an object against which any expressions embedded in the strings 19134 * are evaluated against (typically a scope object). 19135 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 19136 * `context`. 19137 */ 19138 19139 /** 19140 * @ngdoc method 19141 * @name $sce#parseAsUrl 19142 * 19143 * @description 19144 * Shorthand method. `$sce.parseAsUrl(value)` â 19145 * {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`} 19146 * 19147 * @param {string} expression String expression to compile. 19148 * @returns {function(context, locals)} a function which represents the compiled expression: 19149 * 19150 * * `context` â `{object}` â an object against which any expressions embedded in the strings 19151 * are evaluated against (typically a scope object). 19152 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 19153 * `context`. 19154 */ 19155 19156 /** 19157 * @ngdoc method 19158 * @name $sce#parseAsResourceUrl 19159 * 19160 * @description 19161 * Shorthand method. `$sce.parseAsResourceUrl(value)` â 19162 * {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`} 19163 * 19164 * @param {string} expression String expression to compile. 19165 * @returns {function(context, locals)} a function which represents the compiled expression: 19166 * 19167 * * `context` â `{object}` â an object against which any expressions embedded in the strings 19168 * are evaluated against (typically a scope object). 19169 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 19170 * `context`. 19171 */ 19172 19173 /** 19174 * @ngdoc method 19175 * @name $sce#parseAsJs 19176 * 19177 * @description 19178 * Shorthand method. `$sce.parseAsJs(value)` â 19179 * {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`} 19180 * 19181 * @param {string} expression String expression to compile. 19182 * @returns {function(context, locals)} a function which represents the compiled expression: 19183 * 19184 * * `context` â `{object}` â an object against which any expressions embedded in the strings 19185 * are evaluated against (typically a scope object). 19186 * * `locals` â `{object=}` â local variables context object, useful for overriding values in 19187 * `context`. 19188 */ 19189 19190 // Shorthand delegations. 19191 var parse = sce.parseAs, 19192 getTrusted = sce.getTrusted, 19193 trustAs = sce.trustAs; 19194 19195 forEach(SCE_CONTEXTS, function (enumValue, name) { 19196 var lName = lowercase(name); 19197 sce[camelCase("parse_as_" + lName)] = function (expr) { 19198 return parse(enumValue, expr); 19199 }; 19200 sce[camelCase("get_trusted_" + lName)] = function (value) { 19201 return getTrusted(enumValue, value); 19202 }; 19203 sce[camelCase("trust_as_" + lName)] = function (value) { 19204 return trustAs(enumValue, value); 19205 }; 19206 });
19207 19208 return sce; 19209 }]; 19210} 19211 19212/** 19213 * !!! This is an undocumented "private" service !!! 19214 * 19215 * @name $sniffer 19216 * @requires $window 19217 * @requires $document 19218 * 19219 * @property {boolean} history Does the browser support html5 history api ? 19220 * @property {boolean} hashchange Does the browser support hashchange event ? 19221 * @property {boolean} transitions Does the browser support CSS transition events ? 19222 * @property {boolean} animations Does the browser support CSS animation events ? 19223 * 19224 * @description 19225 * This is very simple implementation of testing browser's features. 19226 */ 19227function $SnifferProvider() { 19228 this.$get = ['$window', '$document', function($window, $document) { 19229 var eventSupport = {}, 19230 android = 19231 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), 19232 boxee = /Boxee/i.test(($window.navigator || {}).userAgent), 19233 document = $document[0] || {}, 19234 documentMode = document.documentMode, 19235 vendorPrefix, 19236 vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, 19237 bodyStyle = document.body && document.body.style, 19238 transitions = false, 19239 animations = false, 19240 match; 19241 19242 if (bodyStyle) { 19243 for(var prop in bodyStyle) { 19244 if(match = vendorRegex.exec(prop)) { 19245 vendorPrefix = match[0]; 19246 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); 19247 break; 19248 } 19249 } 19250 19251 if(!vendorPrefix) { 19252 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; 19253 } 19254 19255 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); 19256 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); 19257 19258 if (android && (!transitions||!animations)) { 19259 transitions = isString(document.body.style.webkitTransition); 19260 animations = isString(document.body.style.webkitAnimation); 19261 } 19262 } 19263 19264 19265 return { 19266 // Android has history.pushState, but it does not update location correctly 19267 // so let's not use the history API at all. 19268 // http://code.google.com/p/android/issues/detail?id=17471 19269 // https://github.com/angular/angular.js/issues/904 19270 19271 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has 19272 // so let's not use the history API also 19273 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined 19274 // jshint -W018 19275 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), 19276 // jshint +W018 19277 hashchange: 'onhashchange' in $window && 19278 // IE8 compatible mode lies 19279 (!documentMode || documentMode > 7), 19280 hasEvent: function(event) { 19281 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have 19282 // it. In particular the event is not fired when backspace or delete key are pressed or 19283 // when cut operation is performed. 19284 if (event == 'input' && msie == 9) return false; 19285 19286 if (isUndefined(eventSupport[event])) { 19287 var divElm = document.createElement('div'); 19288 eventSupport[event] = 'on' + event in divElm; 19289 } 19290 19291 return eventSupport[event]; 19292 }, 19293 csp: csp(), 19294 vendorPrefix: vendorPrefix, 19295 transitions : transitions, 19296 animations : animations, 19297 android: android, 19298 msie : msie, 19299 msieDocumentMode: documentMode 19300 }; 19301 }]; 19302} 19303 19304function $TimeoutProvider() { 19305 this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', 19306 function($rootScope, $browser, $q, $exceptionHandler) { 19307 var deferreds = {}; 19308 19309 19310 /** 19311 * @ngdoc service 19312 * @name $timeout 19313 * 19314 * @description 19315 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch 19316 * block and delegates any exceptions to 19317 * {@link ng.$exceptionHandler $exceptionHandler} service. 19318 * 19319 * The return value of registering a timeout function is a promise, which will be resolved when 19320 * the timeout is reached and the timeout function is executed. 19321 * 19322 * To cancel a timeout request, call `$timeout.cancel(promise)`. 19323 * 19324 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to 19325 * synchronously flush the queue of deferred functions. 19326 * 19327 * @param {function()} fn A function, whose execution should be delayed. 19328 * @param {number=} [delay=0] Delay in milliseconds. 19329 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise 19330 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. 19331 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this 19332 * promise will be resolved with is the return value of the `fn` function. 19333 * 19334 */ 19335 function timeout(fn, delay, invokeApply) { 19336 var deferred = $q.defer(), 19337 promise = deferred.promise, 19338 skipApply = (isDefined(invokeApply) && !invokeApply), 19339 timeoutId; 19340 19341 timeoutId = $browser.defer(function() { 19342 try { 19343 deferred.resolve(fn()); 19344 } catch(e) { 19345 deferred.reject(e); 19346 $exceptionHandler(e); 19347 } 19348 finally { 19349 delete deferreds[promise.$$timeoutId]; 19350 } 19351 19352 if (!skipApply) $rootScope.$apply(); 19353 }, delay); 19354 19355 promise.$$timeoutId = timeoutId; 19356 deferreds[timeoutId] = deferred; 19357 19358 return promise; 19359 } 19360 19361 19362 /** 19363 * @ngdoc method 19364 * @name $timeout#cancel 19365 *
19366 * @description 19367 * Cancels a task associated with the `promise`. As a result of this, the promise will be 19368 * resolved with a rejection. 19369 * 19370 * @param {Promise=} promise Promise returned by the `$timeout` function. 19371 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully 19372 * canceled. 19373 */ 19374 timeout.cancel = function(promise) { 19375 if (promise && promise.$$timeoutId in deferreds) { 19376 deferreds[promise.$$timeoutId].reject('canceled'); 19377 delete deferreds[promise.$$timeoutId]; 19378 return $browser.defer.cancel(promise.$$timeoutId); 19379 } 19380 return false; 19381 }; 19382 19383 return timeout; 19384 }]; 19385} 19386 19387// NOTE: The usage of window and document instead of $window and $document here is 19388// deliberate. This service depends on the specific behavior of anchor nodes created by the 19389// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and 19390// cause us to break tests. In addition, when the browser resolves a URL for XHR, it 19391// doesn't know about mocked locations and resolves URLs to the real document - which is 19392// exactly the behavior needed here. There is little value is mocking these out for this 19393// service. 19394var urlParsingNode = document.createElement("a"); 19395var originUrl = urlResolve(window.location.href, true); 19396 19397 19398/** 19399 * 19400 * Implementation Notes for non-IE browsers 19401 * ---------------------------------------- 19402 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, 19403 * results both in the normalizing and parsing of the URL. Normalizing means that a relative 19404 * URL will be resolved into an absolute URL in the context of the application document. 19405 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related 19406 * properties are all populated to reflect the normalized URL. This approach has wide 19407 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See 19408 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html 19409 * 19410 * Implementation Notes for IE 19411 * --------------------------- 19412 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other 19413 * browsers. However, the parsed components will not be set if the URL assigned did not specify 19414 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We 19415 * work around that by performing the parsing in a 2nd step by taking a previously normalized 19416 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the 19417 * properties such as protocol, hostname, port, etc. 19418 * 19419 * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one 19420 * uses the inner HTML approach to assign the URL as part of an HTML snippet - 19421 * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL. 19422 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception. 19423 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that 19424 * method and IE < 8 is unsupported. 19425 * 19426 * References: 19427 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement 19428 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html 19429 * http://url.spec.whatwg.org/#urlutils 19430 * https://github.com/angular/angular.js/pull/2902 19431 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ 19432 * 19433 * @kind function 19434 * @param {string} url The URL to be parsed. 19435 * @description Normalizes and parses a URL. 19436 * @returns {object} Returns the normalized URL as a dictionary. 19437 * 19438 * | member name | Description | 19439 * |---------------|----------------| 19440 * | href | A normalized version of the provided URL if it was not an absolute URL | 19441 * | protocol | The protocol including the trailing colon | 19442 * | host | The host and port (if the port is non-default) of the normalizedUrl | 19443 * | search | The search params, minus the question mark | 19444 * | hash | The hash string, minus the hash symbol 19445 * | hostname | The hostname 19446 * | port | The port, without ":" 19447 * | pathname | The pathname, beginning with "/" 19448 * 19449 */ 19450function urlResolve(url, base) { 19451 var href = url; 19452 19453 if (msie) { 19454 // Normalize before parse. Refer Implementation Notes on why this is 19455 // done in two steps on IE. 19456 urlParsingNode.setAttribute("href", href); 19457 href = urlParsingNode.href; 19458 } 19459 19460 urlParsingNode.setAttribute('href', href); 19461 19462 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils 19463 return { 19464 href: urlParsingNode.href, 19465 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', 19466 host: urlParsingNode.host, 19467 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', 19468 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', 19469 hostname: urlParsingNode.hostname, 19470 port: urlParsingNode.port, 19471 pathname: (urlParsingNode.pathname.charAt(0) === '/') 19472 ? urlParsingNode.pathname 19473 : '/' + urlParsingNode.pathname 19474 }; 19475} 19476 19477/** 19478 * Parse a request URL and determine whether this is a same-origin request as the application document. 19479 * 19480 * @param {string|object} requestUrl The url of the request as a string that will be resolved 19481 * or a parsed URL object. 19482 * @returns {boolean} Whether the request is for the same origin as the application document. 19483 */ 19484function urlIsSameOrigin(requestUrl) { 19485 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; 19486 return (parsed.protocol === originUrl.protocol && 19487 parsed.host === originUrl.host); 19488} 19489 19490/** 19491 * @ngdoc service 19492 * @name $window 19493 * 19494 * @description 19495 * A reference to the browser's `window` object. While `window` 19496 * is globally available in JavaScript, it causes testability problems, because 19497 * it is a global variable. In angular we always refer to it through the 19498 * `$window` service, so it may be overridden, removed or mocked for testing. 19499 * 19500 * Expressions, like the one defined for the `ngClick` directive in the example 19501 * below, are evaluated with respect to the current scope. Therefore, there is 19502 * no risk of inadvertently coding in a dependency on a global value in such an 19503 * expression. 19504 * 19505 * @example 19506 <example module="windowExample"> 19507 <file name="index.html"> 19508 <script> 19509 angular.module('windowExample', [])
19510 .controller('ExampleController', ['$scope', '$window', function ($scope, $window) { 19511 $scope.greeting = 'Hello, World!'; 19512 $scope.doGreeting = function(greeting) { 19513 $window.alert(greeting); 19514 }; 19515 }]); 19516 </script> 19517 <div ng-controller="ExampleController"> 19518 <input type="text" ng-model="greeting" /> 19519 <button ng-click="doGreeting(greeting)">ALERT</button> 19520 </div> 19521 </file> 19522 <file name="protractor.js" type="protractor"> 19523 it('should display the greeting in the input box', function() { 19524 element(by.model('greeting')).sendKeys('Hello, E2E Tests'); 19525 // If we click the button it will block the test runner 19526 // element(':button').click(); 19527 }); 19528 </file> 19529 </example> 19530 */ 19531function $WindowProvider(){ 19532 this.$get = valueFn(window); 19533} 19534 19535/* global currencyFilter: true, 19536 dateFilter: true, 19537 filterFilter: true, 19538 jsonFilter: true, 19539 limitToFilter: true, 19540 lowercaseFilter: true, 19541 numberFilter: true, 19542 orderByFilter: true, 19543 uppercaseFilter: true, 19544 */ 19545 19546/** 19547 * @ngdoc provider 19548 * @name $filterProvider 19549 * @description 19550 * 19551 * Filters are just functions which transform input to an output. However filters need to be 19552 * Dependency Injected. To achieve this a filter definition consists of a factory function which is 19553 * annotated with dependencies and is responsible for creating a filter function. 19554 * 19555 * ```js 19556 * // Filter registration 19557 * function MyModule($provide, $filterProvider) { 19558 * // create a service to demonstrate injection (not always needed) 19559 * $provide.value('greet', function(name){ 19560 * return 'Hello ' + name + '!'; 19561 * }); 19562 * 19563 * // register a filter factory which uses the 19564 * // greet service to demonstrate DI. 19565 * $filterProvider.register('greet', function(greet){ 19566 * // return the filter function which uses the greet service 19567 * // to generate salutation 19568 * return function(text) { 19569 * // filters need to be forgiving so check input validity 19570 * return text && greet(text) || text; 19571 * }; 19572 * }); 19573 * } 19574 * ``` 19575 * 19576 * The filter function is registered with the `$injector` under the filter name suffix with 19577 * `Filter`. 19578 * 19579 * ```js 19580 * it('should be the same instance', inject( 19581 * function($filterProvider) { 19582 * $filterProvider.register('reverse', function(){ 19583 * return ...; 19584 * }); 19585 * }, 19586 * function($filter, reverseFilter) { 19587 * expect($filter('reverse')).toBe(reverseFilter); 19588 * }); 19589 * ``` 19590 * 19591 * 19592 * For more information about how angular filters work, and how to create your own filters, see 19593 * {@link guide/filter Filters} in the Angular Developer Guide. 19594 */ 19595 19596/** 19597 * @ngdoc service 19598 * @name $filter 19599 * @kind function 19600 * @description 19601 * Filters are used for formatting data displayed to the user. 19602 * 19603 * The general syntax in templates is as follows: 19604 * 19605 * {{ expression [| filter_name[:parameter_value] ... ] }} 19606 * 19607 * @param {String} name Name of the filter function to retrieve 19608 * @return {Function} the filter function 19609 * @example 19610 <example name="$filter" module="filterExample"> 19611 <file name="index.html"> 19612 <div ng-controller="MainCtrl"> 19613 <h3>{{ originalText }}</h3> 19614 <h3>{{ filteredText }}</h3> 19615 </div> 19616 </file> 19617 19618 <file name="script.js"> 19619 angular.module('filterExample', []) 19620 .controller('MainCtrl', function($scope, $filter) { 19621 $scope.originalText = 'hello'; 19622 $scope.filteredText = $filter('uppercase')($scope.originalText); 19623 }); 19624 </file> 19625 </example> 19626 */ 19627$FilterProvider.$inject = ['$provide']; 19628function $FilterProvider($provide) { 19629 var suffix = 'Filter'; 19630 19631 /** 19632 * @ngdoc method 19633 * @name $filterProvider#register 19634 * @param {string|Object} name Name of the filter function, or an object map of filters where 19635 * the keys are the filter names and the values are the filter factories. 19636 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map 19637 * of the registered filter instances. 19638 */ 19639 function register(name, factory) { 19640 if(isObject(name)) { 19641 var filters = {};
19642 forEach(name, function(filter, key) { 19643 filters[key] = register(key, filter); 19644 }); 19645 return filters; 19646 } else { 19647 return $provide.factory(name + suffix, factory); 19648 } 19649 } 19650 this.register = register; 19651 19652 this.$get = ['$injector', function($injector) { 19653 return function(name) { 19654 return $injector.get(name + suffix); 19655 }; 19656 }]; 19657 19658 //////////////////////////////////////// 19659 19660 /* global 19661 currencyFilter: false, 19662 dateFilter: false, 19663 filterFilter: false, 19664 jsonFilter: false, 19665 limitToFilter: false, 19666 lowercaseFilter: false, 19667 numberFilter: false, 19668 orderByFilter: false, 19669 uppercaseFilter: false, 19670 */ 19671 19672 register('currency', currencyFilter); 19673 register('date', dateFilter); 19674 register('filter', filterFilter); 19675 register('json', jsonFilter); 19676 register('limitTo', limitToFilter); 19677 register('lowercase', lowercaseFilter); 19678 register('number', numberFilter); 19679 register('orderBy', orderByFilter); 19680 register('uppercase', uppercaseFilter); 19681} 19682 19683/** 19684 * @ngdoc filter 19685 * @name filter 19686 * @kind function 19687 * 19688 * @description 19689 * Selects a subset of items from `array` and returns it as a new array. 19690 * 19691 * @param {Array} array The source array. 19692 * @param {string|Object|function()} expression The predicate to be used for selecting items from 19693 * `array`. 19694 * 19695 * Can be one of: 19696 * 19697 * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against 19698 * the contents of the `array`. All strings or objects with string properties in `array` that contain this string 19699 * will be returned. The predicate can be negated by prefixing the string with `!`. 19700 * 19701 * - `Object`: A pattern object can be used to filter specific properties on objects contained 19702 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items 19703 * which have property `name` containing "M" and property `phone` containing "1". A special 19704 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any 19705 * property of the object. That's equivalent to the simple substring match with a `string` 19706 * as described above. The predicate can be negated by prefixing the string with `!`. 19707 * For Example `{name: "!M"}` predicate will return an array of items which have property `name` 19708 * not containing "M". 19709 * 19710 * - `function(value)`: A predicate function can be used to write arbitrary filters. The function is 19711 * called for each element of `array`. The final result is an array of those elements that 19712 * the predicate returned true for. 19713 * 19714 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in 19715 * determining if the expected value (from the filter expression) and actual value (from 19716 * the object in the array) should be considered a match. 19717 * 19718 * Can be one of: 19719 * 19720 * - `function(actual, expected)`: 19721 * The function will be given the object value and the predicate value to compare and 19722 * should return true if the item should be included in filtered result. 19723 * 19724 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. 19725 * this is essentially strict comparison of expected and actual. 19726 * 19727 * - `false|undefined`: A short hand for a function which will look for a substring match in case 19728 * insensitive way. 19729 * 19730 * @example 19731 <example> 19732 <file name="index.html"> 19733 <div ng-init="friends = [{name:'John', phone:'555-1276'}, 19734 {name:'Mary', phone:'800-BIG-MARY'}, 19735 {name:'Mike', phone:'555-4321'}, 19736 {name:'Adam', phone:'555-5678'}, 19737 {name:'Julie', phone:'555-8765'}, 19738 {name:'Juliette', phone:'555-5678'}]"></div> 19739 19740 Search: <input ng-model="searchText"> 19741 <table id="searchTextResults"> 19742 <tr><th>Name</th><th>Phone</th></tr> 19743 <tr ng-repeat="friend in friends | filter:searchText"> 19744 <td>{{friend.name}}</td> 19745 <td>{{friend.phone}}</td> 19746 </tr> 19747 </table> 19748 <hr> 19749 Any: <input ng-model="search.$"> <br> 19750 Name only <input ng-model="search.name"><br> 19751 Phone only <input ng-model="search.phone"><br> 19752 Equality <input type="checkbox" ng-model="strict"><br> 19753 <table id="searchObjResults"> 19754 <tr><th>
19754Name</th><th>Phone</th></tr> 19755 <tr ng-repeat="friendObj in friends | filter:search:strict"> 19756 <td>{{friendObj.name}}</td> 19757 <td>{{friendObj.phone}}</td> 19758 </tr> 19759 </table> 19760 </file> 19761 <file name="protractor.js" type="protractor"> 19762 var expectFriendNames = function(expectedNames, key) { 19763 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { 19764 arr.forEach(function(wd, i) { 19765 expect(wd.getText()).toMatch(expectedNames[i]); 19766 }); 19767 }); 19768 }; 19769 19770 it('should search across all fields when filtering with a string', function() { 19771 var searchText = element(by.model('searchText')); 19772 searchText.clear(); 19773 searchText.sendKeys('m'); 19774 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); 19775 19776 searchText.clear(); 19777 searchText.sendKeys('76'); 19778 expectFriendNames(['John', 'Julie'], 'friend'); 19779 }); 19780 19781 it('should search in specific fields when filtering with a predicate object', function() { 19782 var searchAny = element(by.model('search.$')); 19783 searchAny.clear(); 19784 searchAny.sendKeys('i'); 19785 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); 19786 }); 19787 it('should use a equal comparison when comparator is true', function() { 19788 var searchName = element(by.model('search.name')); 19789 var strict = element(by.model('strict')); 19790 searchName.clear(); 19791 searchName.sendKeys('Julie'); 19792 strict.click(); 19793 expectFriendNames(['Julie'], 'friendObj'); 19794 }); 19795 </file> 19796 </example> 19797 */ 19798function filterFilter() { 19799 return function(array, expression, comparator) { 19800 if (!isArray(array)) return array; 19801 19802 var comparatorType = typeof(comparator), 19803 predicates = []; 19804 19805 predicates.check = function(value) { 19806 for (var j = 0; j < predicates.length; j++) { 19807 if(!predicates[j](value)) { 19808 return false; 19809 } 19810 } 19811 return true; 19812 }; 19813 19814 if (comparatorType !== 'function') { 19815 if (comparatorType === 'boolean' && comparator) { 19816 comparator = function(obj, text) { 19817 return angular.equals(obj, text); 19818 }; 19819 } else { 19820 comparator = function(obj, text) { 19821 if (obj && text && typeof obj === 'object' && typeof text === 'object') { 19822 for (var objKey in obj) { 19823 if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) && 19824 comparator(obj[objKey], text[objKey])) { 19825 return true; 19826 } 19827 } 19828 return false; 19829 } 19830 text = (''+text).toLowerCase(); 19831 return (''+obj).toLowerCase().indexOf(text) > -1; 19832 }; 19833 } 19834 } 19835 19836 var search = function(obj, text){ 19837 if (typeof text == 'string' && text.charAt(0) === '!') { 19838 return !search(obj, text.substr(1)); 19839 } 19840 switch (typeof obj) { 19841 case "boolean": 19842 case "number": 19843 case "string": 19844 return comparator(obj, text); 19845 case "object": 19846 switch (typeof text) { 19847 case "object": 19848 return comparator(obj, text); 19849 default: 19850 for ( var objKey in obj) { 19851 if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { 19852 return true; 19853 } 19854 } 19855 break; 19856 } 19857 return false; 19858 case "array": 19859 for ( var i = 0; i < obj.length; i++) { 19860 if (search(obj[i], text)) { 19861 return true; 19862 } 19863 } 19864 return false; 19865 default: 19866 return false; 19867 } 19868 }; 19869 switch (typeof expression) { 19870 case "boolean": 19871 case "number": 19872 case "string": 19873 // Set up expression object and fall through 19874 expression = {$:expression}; 19875 // jshint -W086 19876 case "object": 19877 // jshint +W086 19878 for (var key in expression) { 19879 (function(path) { 19880 if (typeof expression[path] === 'undefined') return; 19881 predicates.push(function(value) { 19882 return search(path == '$' ? value : (value && value[path]), expression[path]); 19883 }); 19884 })(key); 19885 } 19886 break; 19887 case 'function': 19888 predicates.push(expression); 19889 break; 19890 default: 19891 return array; 19892 } 19893 var filtered = []; 19894 for ( var j = 0; j < array.length; j++) { 19895 var value = array[j]; 19896 if (predicates.check(value)) { 19897 filtered.push(value); 19898 } 19899 } 19900 return filtered; 19901 }; 19902} 19903 19904/** 19905 * @ngdoc filter 19906 * @name currency 19907 * @kind function 19908 * 19909 * @description 19910 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default 19911 * symbol for current locale is used. 19912 * 19913 * @param {number} amount Input to filter. 19914 * @param {string=} symbol Currency symbol or identifier to be displayed. 19915 * @returns {string} Formatted number. 19916 * 19917 * 19918 * @example 19919 <example module="currencyExample"> 19920 <file name="index.html"> 19921 <script>
19922 angular.module('currencyExample', []) 19923 .controller('ExampleController', ['$scope', function($scope) { 19924 $scope.amount = 1234.56; 19925 }]); 19926 </script> 19927 <div ng-controller="ExampleController"> 19928 <input type="number" ng-model="amount"> <br> 19929 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> 19930 custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span> 19931 </div> 19932 </file> 19933 <file name="protractor.js" type="protractor"> 19934 it('should init with 1234.56', function() { 19935 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); 19936 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56'); 19937 }); 19938 it('should update', function() { 19939 if (browser.params.browser == 'safari') { 19940 // Safari does not understand the minus key. See 19941 // https://github.com/angular/protractor/issues/481 19942 return; 19943 } 19944 element(by.model('amount')).clear(); 19945 element(by.model('amount')).sendKeys('-1234'); 19946 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); 19947 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)'); 19948 }); 19949 </file> 19950 </example> 19951 */ 19952currencyFilter.$inject = ['$locale']; 19953function currencyFilter($locale) { 19954 var formats = $locale.NUMBER_FORMATS; 19955 return function(amount, currencySymbol){ 19956 if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; 19957 return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). 19958 replace(/\u00A4/g, currencySymbol); 19959 }; 19960} 19961 19962/** 19963 * @ngdoc filter 19964 * @name number 19965 * @kind function 19966 * 19967 * @description 19968 * Formats a number as text. 19969 * 19970 * If the input is not a number an empty string is returned. 19971 * 19972 * @param {number|string} number Number to format. 19973 * @param {(number|string)=} fractionSize Number of decimal places to round the number to. 19974 * If this is not provided then the fraction size is computed from the current locale's number 19975 * formatting pattern. In the case of the default locale, it will be 3. 19976 * @returns {string} Number rounded to decimalPlaces and places a â,â after each third digit. 19977 * 19978 * @example 19979 <example module="numberFilterExample"> 19980 <file name="index.html"> 19981 <script> 19982 angular.module('numberFilterExample', []) 19983 .controller('ExampleController', ['$scope', function($scope) { 19984 $scope.val = 1234.56789; 19985 }]); 19986 </script>
19987 <div ng-controller="ExampleController"> 19988 Enter number: <input ng-model='val'><br> 19989 Default formatting: <span id='number-default'>{{val | number}}</span><br> 19990 No fractions: <span>{{val | number:0}}</span><br> 19991 Negative number: <span>{{-val | number:4}}</span> 19992 </div> 19993 </file> 19994 <file name="protractor.js" type="protractor"> 19995 it('should format numbers', function() { 19996 expect(element(by.id('number-default')).getText()).toBe('1,234.568'); 19997 expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); 19998 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); 19999 }); 20000 20001 it('should update', function() { 20002 element(by.model('val')).clear(); 20003 element(by.model('val')).sendKeys('3374.333'); 20004 expect(element(by.id('number-default')).getText()).toBe('3,374.333'); 20005 expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); 20006 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); 20007 }); 20008 </file> 20009 </example> 20010 */ 20011 20012 20013numberFilter.$inject = ['$locale']; 20014function numberFilter($locale) { 20015 var formats = $locale.NUMBER_FORMATS; 20016 return function(number, fractionSize) { 20017 return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, 20018 fractionSize); 20019 }; 20020} 20021 20022var DECIMAL_SEP = '.'; 20023function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { 20024 if (number == null || !isFinite(number) || isObject(number)) return ''; 20025 20026 var isNegative = number < 0; 20027 number = Math.abs(number); 20028 var numStr = number + '', 20029 formatedText = '', 20030 parts = []; 20031 20032 var hasExponent = false; 20033 if (numStr.indexOf('e') !== -1) { 20034 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); 20035 if (match && match[2] == '-' && match[3] > fractionSize + 1) { 20036 numStr = '0'; 20037 number = 0; 20038 } else { 20039 formatedText = numStr; 20040 hasExponent = true; 20041 } 20042 } 20043 20044 if (!hasExponent) { 20045 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; 20046 20047 // determine fractionSize if it is not specified 20048 if (isUndefined(fractionSize)) { 20049 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); 20050 } 20051 20052 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics 20053 // inspired by: 20054 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round 20055 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); 20056 20057 if (number === 0) { 20058 isNegative = false; 20059 } 20060 20061 var fraction = ('' + number).split(DECIMAL_SEP); 20062 var whole = fraction[0]; 20063 fraction = fraction[1] || ''; 20064 20065 var i, pos = 0, 20066 lgroup = pattern.lgSize, 20067 group = pattern.gSize; 20068 20069 if (whole.length >= (lgroup + group)) { 20070 pos = whole.length - lgroup; 20071 for (i = 0; i < pos; i++) { 20072 if ((pos - i)%group === 0 && i !== 0) { 20073 formatedText += groupSep; 20074 } 20075 formatedText += whole.charAt(i); 20076 } 20077 } 20078 20079 for (i = pos; i < whole.length; i++) { 20080 if ((whole.length - i)%lgroup === 0 && i !== 0) { 20081 formatedText += groupSep; 20082 } 20083 formatedText += whole.charAt(i); 20084 } 20085 20086 // format fraction part. 20087 while(fraction.length < fractionSize) { 20088 fraction += '0'; 20089 } 20090 20091 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); 20092 } else { 20093 20094 if (fractionSize > 0 && number > -1 && number < 1) { 20095 formatedText = number.toFixed(fractionSize); 20096 } 20097 } 20098 20099 parts.push(isNegative ? pattern.negPre : pattern.posPre); 20100 parts.push(formatedText); 20101 parts.push(isNegative ? pattern.negSuf : pattern.posSuf); 20102 return parts.join(''); 20103} 20104 20105function padNumber(num, digits, trim) { 20106 var neg = ''; 20107 if (num < 0) { 20108 neg = '-'; 20109 num = -num; 20110 } 20111 num = '' + num; 20112 while(num.length < digits) num = '0' + num; 20113 if (trim) 20114 num = num.substr(num.length - digits); 20115 return neg + num; 20116} 20117 20118 20119function dateGetter(name, size, offset, trim) { 20120 offset = offset || 0; 20121 return function(date) { 20122 var value = date['get' + name](); 20123 if (offset > 0 || value > -offset) 20124 value += offset; 20125 if (value === 0 && offset == -12 ) value = 12; 20126 return padNumber(value, size, trim); 20127 }; 20128} 20129 20130function dateStrGetter(name, shortForm) { 20131 return function(date, formats) { 20132 var value = date['get' + name](); 20133 var get = uppercase(shortForm ? ('SHORT' + name) : name); 20134 20135 return formats[get][value]; 20136 }; 20137} 20138 20139function timeZoneGetter(date) { 20140 var zone = -1 * date.getTimezoneOffset(); 20141 var paddedZone = (zone >= 0) ? "+" : ""; 20142 20143 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + 20144 padNumber(Math.abs(zone % 60), 2); 20145 20146 return paddedZone; 20147} 20148 20149function ampmGetter(date, formats) { 20150 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; 20151} 20152 20153var DATE_FORMATS = { 20154 yyyy: dateGetter('FullYear', 4), 20155 yy: dateGetter('FullYear', 2, 0, true), 20156 y: dateGetter('FullYear', 1), 20157 MMMM: dateStrGetter('Month'), 20158 MMM: dateStrGetter('Month', true), 20159 MM: dateGetter('Month', 2, 1), 20160 M: dateGetter('Month', 1, 1), 20161 dd: dateGetter('Date', 2), 20162 d: dateGetter('Date', 1), 20163 HH: dateGetter('Hours', 2), 20164 H: dateGetter('Hours', 1), 20165 hh: dateGetter('Hours', 2, -12), 20166 h: dateGetter('Hours', 1, -12), 20167 mm: dateGetter('Minutes', 2), 20168 m: dateGetter('Minutes', 1), 20169 ss: dateGetter('Seconds', 2), 20170 s: dateGetter('Seconds', 1), 20171 // while ISO 8601 requires fractions to be prefixed with `.` or `,` 20172 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions 20173 sss: dateGetter('Milliseconds', 3), 20174 EEEE: dateStrGetter('Day'), 20175 EEE: dateStrGetter('Day', true), 20176 a: ampmGetter, 20177 Z: timeZoneGetter 20178}; 20179 20180var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, 20181 NUMBER_STRING = /^\-?\d+$/; 20182 20183/** 20184 * @ngdoc filter 20185 * @name date 20186 * @kind function 20187 * 20188 * @description 20189 * Formats `date` to a string based on the requested `format`. 20190 * 20191 * `format` string can be composed of the following elements: 20192 * 20193 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) 20194 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) 20195 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) 20196 * * `'MMMM'`: Month in year (January-December) 20197 * * `'MMM'`: Month in year (Jan-Dec) 20198 * * `'MM'`: Month in year, padded (01-12) 20199 * * `'M'`: Month in year (1-12) 20200 * * `'dd'`: Day in month, padded (01-31) 20201 * * `'d'`: Day in month (1-31) 20202 * * `'EEEE'`: Day in Week,(Sunday-Saturday) 20203 * * `'EEE'`: Day in Week, (Sun-Sat) 20204 * * `'HH'`: Hour in day, padded (00-23) 20205 * * `'H'`: Hour in day (0-23) 20206 * * `'hh'`: Hour in am/pm, padded (01-12) 20207 * * `'h'`: Hour in am/pm, (1-12) 20208 * * `'mm'`: Minute in hour, padded (00-59) 20209 * * `'m'`: Minute in hour (0-59) 20210 * * `'ss'`: Second in minute, padded (00-59) 20211 * * `'s'`: Second in minute (0-59) 20212 * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) 20213 * * `'a'`: am/pm marker 20214 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) 20215 * 20216 * `format` string can also be one of the following predefined 20217 * {@link guide/i18n localizable formats}: 20218 * 20219 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale 20220 * (e.g. Sep 3, 2010 12:05:08 pm) 20221 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) 20222 * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale 20223 * (e.g. Friday, September 3, 2010) 20224 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) 20225 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) 20226 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) 20227 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) 20228 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) 20229 * 20230 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. 20231 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence 20232 * (e.g. `"h 'o''clock'"`). 20233 * 20234 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or 20235 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its 20236 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is 20237 * specified in the string input, the time is considered to be in the local timezone. 20238 * @param {string=} format Formatting rules (see Description). If not specified, 20239 * `mediumDate` is used. 20240 * @returns {string} Formatted string or the input if input is not recognized as date/millis. 20241 * 20242 * @example 20243 <example> 20244 <file name="index.html">
20245 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>: 20246 <span>{{1288323623006 | date:'medium'}}</span><br> 20247 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: 20248 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br> 20249 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: 20250 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br> 20251 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>: 20252 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br> 20253 </file> 20254 <file name="protractor.js" type="protractor"> 20255 it('should format date', function() { 20256 expect(element(by.binding("1288323623006 | date:'medium'")).getText()). 20257 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); 20258 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). 20259 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); 20260 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). 20261 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); 20262 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). 20263 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); 20264 }); 20265 </file> 20266 </example> 20267 */ 20268dateFilter.$inject = ['$locale']; 20269function dateFilter($locale) { 20270 20271 20272 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; 20273 // 1 2 3 4 5 6 7 8 9 10 11 20274 function jsonStringToDate(string) { 20275 var match; 20276 if (match = string.match(R_ISO8601_STR)) { 20277 var date = new Date(0), 20278 tzHour = 0, 20279 tzMin = 0, 20280 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, 20281 timeSetter = match[8] ? date.setUTCHours : date.setHours; 20282 20283 if (match[9]) { 20284 tzHour = int(match[9] + match[10]); 20285 tzMin = int(match[9] + match[11]); 20286 } 20287 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); 20288 var h = int(match[4]||0) - tzHour; 20289 var m = int(match[5]||0) - tzMin; 20290 var s = int(match[6]||0); 20291 var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); 20292 timeSetter.call(date, h, m, s, ms); 20293 return date; 20294 } 20295 return string; 20296 } 20297 20298 20299 return function(date, format) { 20300 var text = '', 20301 parts = [], 20302 fn, match; 20303 20304 format = format || 'mediumDate'; 20305 format = $locale.DATETIME_FORMATS[format] || format; 20306 if (isString(date)) { 20307 date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date); 20308 } 20309 20310 if (isNumber(date)) { 20311 date = new Date(date); 20312 } 20313 20314 if (!isDate(date)) { 20315 return date; 20316 } 20317 20318 while(format) { 20319 match = DATE_FORMATS_SPLIT.exec(format); 20320 if (match) { 20321 parts = concat(parts, match, 1); 20322 format = parts.pop(); 20323 } else { 20324 parts.push(format); 20325 format = null; 20326 } 20327 } 20328 20329 forEach(parts, function(value){ 20330 fn = DATE_FORMATS[value]; 20331 text += fn ? fn(date, $locale.DATETIME_FORMATS) 20332 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); 20333 }); 20334 20335 return text; 20336 }; 20337} 20338 20339 20340/** 20341 * @ngdoc filter 20342 * @name json 20343 * @kind function 20344 * 20345 * @description 20346 * Allows you to convert a JavaScript object into JSON string. 20347 * 20348 * This filter is mostly useful for debugging. When using the double curly {{value}} notation 20349 * the binding is automatically converted to JSON. 20350 * 20351 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. 20352 * @returns {string} JSON string. 20353 * 20354 * 20355 * @example 20356 <example> 20357 <file name="index.html"> 20358 <pre>{{ {'name':'value'} | json }}</pre> 20359 </file> 20360 <file name="protractor.js" type="protractor"> 20361 it('should jsonify filtered objects', function() { 20362 expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/); 20363 }); 20364 </file> 20365 </example> 20366 * 20367 */ 20368function jsonFilter() { 20369 return function(object) { 20370 return toJson(object, true); 20371 }; 20372} 20373 20374 20375/** 20376 * @ngdoc filter 20377 * @name lowercase 20378 * @kind function 20379 * @description 20380 * Converts string to lowercase. 20381 * @see angular.lowercase 20382 */ 20383var lowercaseFilter = valueFn(lowercase); 20384 20385 20386/** 20387 * @ngdoc filter 20388 * @name uppercase 20389 * @kind function 20390 * @description 20391 * Converts string to uppercase. 20392 * @see angular.uppercase 20393 */ 20394var uppercaseFilter = valueFn(uppercase); 20395 20396/** 20397 * @ngdoc filter 20398 * @name limitTo 20399 * @kind function 20400 * 20401 * @description 20402 * Creates a new array or string containing only a specified number of elements. The elements 20403 * are taken from either the beginning or the end of the source array or string, as specified by 20404 * the value and sign (positive or negative) of `limit`. 20405 * 20406 * @param {Array|string} input Source array or string to be limited. 20407 * @param {string|number} limit The length of the returned array or string. If the `limit` number 20408 * is positive, `limit` number of items from the beginning of the source array/string are copied. 20409 * If the number is negative, `limit` number of items from the end of the source array/string 20410 * are copied. The `limit` will be trimmed if it exceeds `array.length` 20411 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array 20412 * had less than `limit` elements. 20413 * 20414 * @example 20415 <example module="limitToExample"> 20416 <file name="index.html"> 20417 <script> 20418 angular.module('limitToExample', [])
20419 .controller('ExampleController', ['$scope', function($scope) { 20420 $scope.numbers = [1,2,3,4,5,6,7,8,9]; 20421 $scope.letters = "abcdefghi"; 20422 $scope.numLimit = 3; 20423 $scope.letterLimit = 3; 20424 }]); 20425 </script> 20426 <div ng-controller="ExampleController"> 20427 Limit {{numbers}} to: <input type="integer" ng-model="numLimit"> 20428 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> 20429 Limit {{letters}} to: <input type="integer" ng-model="letterLimit"> 20430 <p>Output letters: {{ letters | limitTo:letterLimit }}</p> 20431 </div> 20432 </file> 20433 <file name="protractor.js" type="protractor"> 20434 var numLimitInput = element(by.model('numLimit')); 20435 var letterLimitInput = element(by.model('letterLimit')); 20436 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); 20437 var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); 20438 20439 it('should limit the number array to first three items', function() { 20440 expect(numLimitInput.getAttribute('value')).toBe('3'); 20441 expect(letterLimitInput.getAttribute('value')).toBe('3'); 20442 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); 20443 expect(limitedLetters.getText()).toEqual('Output letters: abc'); 20444 }); 20445 20446 it('should update the output when -3 is entered', function() { 20447 numLimitInput.clear(); 20448 numLimitInput.sendKeys('-3'); 20449 letterLimitInput.clear(); 20450 letterLimitInput.sendKeys('-3'); 20451 expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); 20452 expect(limitedLetters.getText()).toEqual('Output letters: ghi'); 20453 }); 20454 20455 it('should not exceed the maximum size of input array', function() { 20456 numLimitInput.clear(); 20457 numLimitInput.sendKeys('100'); 20458 letterLimitInput.clear(); 20459 letterLimitInput.sendKeys('100'); 20460 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); 20461 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); 20462 }); 20463 </file> 20464 </example> 20465 */ 20466function limitToFilter(){ 20467 return function(input, limit) { 20468 if (!isArray(input) && !isString(input)) return input; 20469 20470 if (Math.abs(Number(limit)) === Infinity) { 20471 limit = Number(limit); 20472 } else { 20473 limit = int(limit); 20474 } 20475 20476 if (isString(input)) { 20477 //NaN check on limit 20478 if (limit) { 20479 return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length); 20480 } else { 20481 return ""; 20482 } 20483 } 20484 20485 var out = [], 20486 i, n; 20487 20488 // if abs(limit) exceeds maximum length, trim it 20489 if (limit > input.length) 20490 limit = input.length; 20491 else if (limit < -input.length) 20492 limit = -input.length; 20493 20494 if (limit > 0) { 20495 i = 0; 20496 n = limit; 20497 } else { 20498 i = input.length + limit; 20499 n = input.length; 20500 } 20501 20502 for (; i<n; i++) { 20503 out.push(input[i]); 20504 } 20505 20506 return out; 20507 }; 20508} 20509 20510/** 20511 * @ngdoc filter 20512 * @name orderBy 20513 * @kind function 20514 * 20515 * @description 20516 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically 20517 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted 20518 * correctly, make sure they are actually being saved as numbers and not strings. 20519 * 20520 * @param {Array} array The array to sort. 20521 * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be 20522 * used by the comparator to determine the order of elements. 20523 * 20524 * Can be one of: 20525 * 20526 * - `function`: Getter function. The result of this function will be sorted using the 20527 * `<`, `=`, `>` operator. 20528 * - `string`: An Angular expression. The result of this expression is used to compare elements 20529 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by 20530 * 3 first characters of a property called `name`). The result of a constant expression 20531 * is interpreted as a property name to be used in comparisons (for example `"special name"` 20532 * to sort object by the value of their `special name` property). An expression can be 20533 * optionally prefixed with `+` or `-` to control ascending or descending sort order 20534 * (for example, `+name` or `-name`). 20535 * - `Array`: An array of function or string predicates. The first predicate in the array 20536 * is used for sorting, but when two items are equivalent, the next predicate is used. 20537 * 20538 * @param {boolean=} reverse Reverse the order of the array. 20539 * @returns {Array} Sorted copy of the source array. 20540 * 20541 * @example 20542 <example module="orderByExample"> 20543 <file name="index.html"> 20544 <script> 20545 angular.module('orderByExample', [])
20546 .controller('ExampleController', ['$scope', function($scope) { 20547 $scope.friends = 20548 [{name:'John', phone:'555-1212', age:10}, 20549 {name:'Mary', phone:'555-9876', age:19}, 20550 {name:'Mike', phone:'555-4321', age:21}, 20551 {name:'Adam', phone:'555-5678', age:35}, 20552 {name:'Julie', phone:'555-8765', age:29}]; 20553 $scope.predicate = '-age'; 20554 }]); 20555 </script> 20556 <div ng-controller="ExampleController"> 20557 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre> 20558 <hr/> 20559 [ <a href="" ng-click="predicate=''">unsorted</a> ] 20560 <table class="friend"> 20561 <tr> 20562 <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a> 20563 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th> 20564 <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th> 20565 <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th> 20566 </tr> 20567 <tr ng-repeat="friend in friends | orderBy:predicate:reverse"> 20568 <td>{{friend.name}}</td> 20569 <td>{{friend.phone}}</td> 20570 <td>{{friend.age}}</td> 20571 </tr> 20572 </table> 20573 </div> 20574 </file> 20575 </example> 20576 * 20577 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the 20578 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the 20579 * desired parameters. 20580 * 20581 * Example: 20582 * 20583 * @example 20584 <example module="orderByExample"> 20585 <file name="index.html">
20586 <div ng-controller="ExampleController"> 20587 <table class="friend"> 20588 <tr> 20589 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a> 20590 (<a href="" ng-click="order('-name',false)">^</a>)</th> 20591 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th> 20592 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th> 20593 </tr> 20594 <tr ng-repeat="friend in friends"> 20595 <td>{{friend.name}}</td> 20596 <td>{{friend.phone}}</td> 20597 <td>{{friend.age}}</td> 20598 </tr> 20599 </table> 20600 </div> 20601 </file> 20602 20603 <file name="script.js"> 20604 angular.module('orderByExample', []) 20605 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { 20606 var orderBy = $filter('orderBy'); 20607 $scope.friends = [ 20608 { name: 'John', phone: '555-1212', age: 10 }, 20609 { name: 'Mary', phone: '555-9876', age: 19 }, 20610 { name: 'Mike', phone: '555-4321', age: 21 }, 20611 { name: 'Adam', phone: '555-5678', age: 35 }, 20612 { name: 'Julie', phone: '555-8765', age: 29 } 20613 ]; 20614 $scope.order = function(predicate, reverse) { 20615 $scope.friends = orderBy($scope.friends, predicate, reverse); 20616 }; 20617 $scope.order('-age',false); 20618 }]); 20619 </file> 20620</example> 20621 */ 20622orderByFilter.$inject = ['$parse']; 20623function orderByFilter($parse){ 20624 return function(array, sortPredicate, reverseOrder) { 20625 if (!(isArrayLike(array))) return array; 20626 if (!sortPredicate) return array; 20627 sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; 20628 sortPredicate = map(sortPredicate, function(predicate){ 20629 var descending = false, get = predicate || identity; 20630 if (isString(predicate)) { 20631 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { 20632 descending = predicate.charAt(0) == '-'; 20633 predicate = predicate.substring(1); 20634 } 20635 get = $parse(predicate); 20636 if (get.constant) { 20637 var key = get(); 20638 return reverseComparator(function(a,b) { 20639 return compare(a[key], b[key]); 20640 }, descending); 20641 } 20642 } 20643 return reverseComparator(function(a,b){ 20644 return compare(get(a),get(b)); 20645 }, descending); 20646 }); 20647 var arrayCopy = []; 20648 for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } 20649 return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); 20650 20651 function comparator(o1, o2){ 20652 for ( var i = 0; i < sortPredicate.length; i++) { 20653 var comp = sortPredicate[i](o1, o2); 20654 if (comp !== 0) return comp; 20655 } 20656 return 0; 20657 } 20658 function reverseComparator(comp, descending) { 20659 return toBoolean(descending) 20660 ? function(a,b){return comp(b,a);} 20661 : comp; 20662 } 20663 function compare(v1, v2){ 20664 var t1 = typeof v1; 20665 var t2 = typeof v2; 20666 if (t1 == t2) { 20667 if (isDate(v1) && isDate(v2)) { 20668 v1 = v1.valueOf(); 20669 v2 = v2.valueOf(); 20670 } 20671 if (t1 == "string") { 20672 v1 = v1.toLowerCase(); 20673 v2 = v2.toLowerCase(); 20674 } 20675 if (v1 === v2) return 0; 20676 return v1 < v2 ? -1 : 1; 20677 } else { 20678 return t1 < t2 ? -1 : 1; 20679 } 20680 } 20681 }; 20682} 20683 20684function ngDirective(directive) { 20685 if (isFunction(directive)) { 20686 directive = { 20687 link: directive 20688 }; 20689 } 20690 directive.restrict = directive.restrict || 'AC'; 20691 return valueFn(directive); 20692} 20693 20694/** 20695 * @ngdoc directive 20696 * @name a 20697 * @restrict E 20698 * 20699 * @description 20700 * Modifies the default behavior of the html A tag so that the default action is prevented when 20701 * the href attribute is empty. 20702 * 20703 * This change permits the easy creation of action links with the `ngClick` directive 20704 * without changing the location or causing page reloads, e.g.: 20705 * `<a href="" ng-click="list.addItem()">Add Item</a>` 20706 */ 20707var htmlAnchorDirective = valueFn({ 20708 restrict: 'E',
20709 compile: function(element, attr) { 20710 20711 if (msie <= 8) { 20712 20713 // turn <a href ng-click="..">link</a> into a stylable link in IE 20714 // but only if it doesn't have name attribute, in which case it's an anchor 20715 if (!attr.href && !attr.name) { 20716 attr.$set('href', ''); 20717 } 20718 20719 // add a comment node to anchors to workaround IE bug that causes element content to be reset 20720 // to new attribute content if attribute is updated with value containing @ and element also 20721 // contains value with @ 20722 // see issue #1949 20723 element.append(document.createComment('IE fix')); 20724 } 20725 20726 if (!attr.href && !attr.xlinkHref && !attr.name) { 20727 return function(scope, element) { 20728 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. 20729 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? 20730 'xlink:href' : 'href'; 20731 element.on('click', function(event){ 20732 // if we have no href url, then don't navigate anywhere. 20733 if (!element.attr(href)) { 20734 event.preventDefault(); 20735 } 20736 }); 20737 }; 20738 } 20739 } 20740}); 20741 20742/** 20743 * @ngdoc directive 20744 * @name ngHref 20745 * @restrict A 20746 * @priority 99 20747 * 20748 * @description 20749 * Using Angular markup like `{{hash}}` in an href attribute will 20750 * make the link go to the wrong URL if the user clicks it before 20751 * Angular has a chance to replace the `{{hash}}` markup with its 20752 * value. Until Angular replaces the markup the link will be broken 20753 * and will most likely return a 404 error. 20754 * 20755 * The `ngHref` directive solves this problem. 20756 * 20757 * The wrong way to write it: 20758 * ```html 20759 * <a href="http://www.gravatar.com/avatar/{{hash}}"/> 20760 * ``` 20761 * 20762 * The correct way to write it: 20763 * ```html 20764 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/> 20765 * ``` 20766 * 20767 * @element A 20768 * @param {template} ngHref any string which can contain `{{}}` markup. 20769 * 20770 * @example 20771 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes 20772 * in links and their different behaviors: 20773 <example> 20774 <file name="index.html"> 20775 <input ng-model="value" /><br /> 20776 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> 20777 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> 20778 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> 20779 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> 20780 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> 20781 <a id="link-6" ng-href="{{value}}">link</a> (link, change location) 20782 </file> 20783 <file name="protractor.js" type="protractor"> 20784 it('should execute ng-click but not reload when href without value', function() { 20785 element(by.id('link-1')).click(); 20786 expect(element(by.model('value')).getAttribute('value')).toEqual('1'); 20787 expect(element(by.id('link-1')).getAttribute('href')).toBe(''); 20788 }); 20789 20790 it('should execute ng-click but not reload when href empty string', function() { 20791 element(by.id('link-2')).click(); 20792 expect(element(by.model('value')).getAttribute('value')).toEqual('2'); 20793 expect(element(by.id('link-2')).getAttribute('href')).toBe(''); 20794 }); 20795 20796 it('should execute ng-click and change url when ng-href specified', function() { 20797 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); 20798 20799 element(by.id('link-3')).click(); 20800 20801 // At this point, we navigate away from an Angular page, so we need 20802 // to use browser.driver to get the base webdriver. 20803 20804 browser.wait(function() { 20805 return browser.driver.getCurrentUrl().then(function(url) { 20806 return url.match(/\/123$/); 20807 }); 20808 }, 5000, 'page should navigate to /123'); 20809 }); 20810 20811 xit('should execute ng-click but not reload when href empty string and name specified', function() {
20812 element(by.id('link-4')).click(); 20813 expect(element(by.model('value')).getAttribute('value')).toEqual('4'); 20814 expect(element(by.id('link-4')).getAttribute('href')).toBe(''); 20815 }); 20816 20817 it('should execute ng-click but not reload when no href but name specified', function() { 20818 element(by.id('link-5')).click(); 20819 expect(element(by.model('value')).getAttribute('value')).toEqual('5'); 20820 expect(element(by.id('link-5')).getAttribute('href')).toBe(null); 20821 }); 20822 20823 it('should only change url when only ng-href', function() { 20824 element(by.model('value')).clear(); 20825 element(by.model('value')).sendKeys('6'); 20826 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); 20827 20828 element(by.id('link-6')).click(); 20829 20830 // At this point, we navigate away from an Angular page, so we need 20831 // to use browser.driver to get the base webdriver. 20832 browser.wait(function() { 20833 return browser.driver.getCurrentUrl().then(function(url) { 20834 return url.match(/\/6$/); 20835 }); 20836 }, 5000, 'page should navigate to /6'); 20837 }); 20838 </file> 20839 </example> 20840 */ 20841 20842/** 20843 * @ngdoc directive 20844 * @name ngSrc 20845 * @restrict A 20846 * @priority 99 20847 * 20848 * @description 20849 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't 20850 * work right: The browser will fetch from the URL with the literal 20851 * text `{{hash}}` until Angular replaces the expression inside 20852 * `{{hash}}`. The `ngSrc` directive solves this problem. 20853 * 20854 * The buggy way to write it: 20855 * ```html 20856 * <img src="http://www.gravatar.com/avatar/{{hash}}"/> 20857 * ``` 20858 * 20859 * The correct way to write it: 20860 * ```html 20861 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/> 20862 * ``` 20863 * 20864 * @element IMG 20865 * @param {template} ngSrc any string which can contain `{{}}` markup. 20866 */ 20867 20868/** 20869 * @ngdoc directive 20870 * @name ngSrcset 20871 * @restrict A 20872 * @priority 99 20873 * 20874 * @description 20875 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't 20876 * work right: The browser will fetch from the URL with the literal 20877 * text `{{hash}}` until Angular replaces the expression inside 20878 * `{{hash}}`. The `ngSrcset` directive solves this problem. 20879 * 20880 * The buggy way to write it: 20881 * ```html 20882 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/> 20883 * ``` 20884 * 20885 * The correct way to write it: 20886 * ```html 20887 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/> 20888 * ``` 20889 * 20890 * @element IMG 20891 * @param {template} ngSrcset any string which can contain `{{}}` markup. 20892 */ 20893 20894/** 20895 * @ngdoc directive 20896 * @name ngDisabled 20897 * @restrict A 20898 * @priority 100 20899 * 20900 * @description 20901 * 20902 * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: 20903 * ```html 20904 * <div ng-init="scope = { isDisabled: false }"> 20905 * <button disabled="{{scope.isDisabled}}">Disabled</button> 20906 * </div> 20907 * ``` 20908 * 20909 * The HTML specification does not require browsers to preserve the values of boolean attributes 20910 * such as disabled. (Their presence means true and their absence means false.) 20911 * If we put an Angular interpolation expression into such an attribute then the 20912 * binding information would be lost when the browser removes the attribute. 20913 * The `ngDisabled` directive solves this problem for the `disabled` attribute. 20914 * This complementary directive is not removed by the browser and so provides 20915 * a permanent reliable place to store the binding information. 20916 * 20917 * @example 20918 <example> 20919 <file name="index.html"> 20920 Click me to toggle: <input type="checkbox" ng-model="checked"><br/> 20921 <button ng-model="button" ng-disabled="checked">Button</button> 20922 </file> 20923 <file name="protractor.js" type="protractor"> 20924 it('should toggle button', function() { 20925 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); 20926 element(by.model('checked')).click(); 20927 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); 20928 }); 20929 </file> 20930 </example> 20931 * 20932 * @element INPUT 20933 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, 20934 * then special attribute "disabled" will be set on the element 20935 */ 20936 20937 20938/** 20939 * @ngdoc directive 20940 * @name ngChecked 20941 * @restrict A 20942 * @priority 100 20943 * 20944 * @description 20945 * The HTML specification does not require browsers to preserve the values of boolean attributes 20946 * such as checked. (Their presence means true and their absence means false.) 20947 * If we put an Angular interpolation expression into such an attribute then the 20948 * binding information would be lost when the browser removes the attribute. 20949 * The `ngChecked` directive solves this problem for the `checked` attribute. 20950 * This complementary directive is not removed by the browser and so provides 20951 * a permanent reliable place to store the binding information. 20952 * @example 20953 <example> 20954 <file name="index.html"> 20955 Check me to check both: <input type="checkbox" ng-model="master"><br/> 20956 <input id="checkSlave" type="checkbox" ng-checked="master"> 20957 </file> 20958 <file name="protractor.js" type="protractor"> 20959 it('should check both checkBoxes', function() { 20960 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20961 element(by.model('master')).click(); 20962 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); 20963 }); 20964 </file> 20965 </example> 20966 * 20967 * @element INPUT 20968 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, 20969 * then special attribute "checked" will be set on the element 20970 */ 20971 20972 20973/** 20974 * @ngdoc directive 20975 * @name ngReadonly 20976 * @restrict A 20977 * @priority 100 20978 * 20979 * @description 20980 * The HTML specification does not require browsers to preserve the values of boolean attributes 20981 * such as readonly. (Their presence means true and their absence means false.) 20982 * If we put an Angular interpolation expression into such an attribute then the 20983 * binding information would be lost when the browser removes the attribute. 20984 * The `ngReadonly` directive solves this problem for the `readonly` attribute. 20985 * This complementary directive is not removed by the browser and so provides 20986 * a permanent reliable place to store the binding information. 20987 * @example 20988 <example> 20989 <file name="index.html"> 20990 Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/> 20991 <input type="text" ng-readonly="checked" value="I'm Angular"/> 20992 </file> 20993 <file name="protractor.js" type="protractor"> 20994 it('should toggle readonly attr', function() { 20995 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); 20996 element(by.model('checked')).click(); 20997 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); 20998 }); 20999 </file> 21000 </example> 21001 * 21002 * @element INPUT 21003 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, 21004 * then special attribute "readonly" will be set on the element 21005 */ 21006 21007 21008/** 21009 * @ngdoc directive 21010 * @name ngSelected 21011 * @restrict A 21012 * @priority 100 21013 * 21014 * @description 21015 * The HTML specification does not require browsers to preserve the values of boolean attributes 21016 * such as selected. (Their presence means true and their absence means false.) 21017 * If we put an Angular interpolation expression into such an attribute then the 21018 * binding information would be lost when the browser removes the attribute. 21019 * The `ngSelected` directive solves this problem for the `selected` attribute. 21020 * This complementary directive is not removed by the browser and so provides 21021 * a permanent reliable place to store the binding information. 21022 * 21023 * @example 21024 <example> 21025 <file name="index.html"> 21026 Check me to select: <input type="checkbox" ng-model="selected"><br/> 21027 <select> 21028 <option>Hello!</option> 21029 <option id="greet" ng-selected="selected">Greetings!</option> 21030 </select> 21031 </file> 21032 <file name="protractor.js" type="protractor"> 21033 it('should select Greetings!', function() { 21034 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); 21035 element(by.model('selected')).click(); 21036 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); 21037 }); 21038 </file> 21039 </example> 21040 * 21041 * @element OPTION 21042 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, 21043 * then special attribute "selected" will be set on the element 21044 */ 21045 21046/** 21047 * @ngdoc directive 21048 * @name ngOpen 21049 * @restrict A 21050 * @priority 100 21051 * 21052 * @description 21053 * The HTML specification does not require browsers to preserve the values of boolean attributes 21054 * such as open. (Their presence means true and their absence means false.) 21055 * If we put an Angular interpolation expression into such an attribute then the 21056 * binding information would be lost when the browser removes the attribute. 21057 * The `ngOpen` directive solves this problem for the `open` attribute. 21058 * This complementary directive is not removed by the browser and so provides 21059 * a permanent reliable place to store the binding information. 21060 * @example 21061 <example> 21062 <file name="index.html"> 21063 Check me check multiple: <input type="checkbox" ng-model="open"><br/> 21064 <details id="details" ng-open="open"> 21065 <summary>Show/Hide me</summary> 21066 </details> 21067 </file> 21068 <file name="protractor.js" type="protractor"> 21069 it('should toggle open', function() { 21070 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
21071 element(by.model('open')).click(); 21072 expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); 21073 }); 21074 </file> 21075 </example> 21076 * 21077 * @element DETAILS 21078 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, 21079 * then special attribute "open" will be set on the element 21080 */ 21081 21082var ngAttributeAliasDirectives = {}; 21083 21084 21085// boolean attrs are evaluated 21086forEach(BOOLEAN_ATTR, function(propName, attrName) { 21087 // binding to multiple is not supported 21088 if (propName == "multiple") return; 21089 21090 var normalized = directiveNormalize('ng-' + attrName); 21091 ngAttributeAliasDirectives[normalized] = function() { 21092 return { 21093 priority: 100, 21094 link: function(scope, element, attr) { 21095 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { 21096 attr.$set(attrName, !!value); 21097 }); 21098 } 21099 }; 21100 }; 21101}); 21102 21103 21104// ng-src, ng-srcset, ng-href are interpolated 21105forEach(['src', 'srcset', 'href'], function(attrName) { 21106 var normalized = directiveNormalize('ng-' + attrName); 21107 ngAttributeAliasDirectives[normalized] = function() { 21108 return { 21109 priority: 99, // it needs to run after the attributes are interpolated 21110 link: function(scope, element, attr) { 21111 var propName = attrName, 21112 name = attrName; 21113 21114 if (attrName === 'href' && 21115 toString.call(element.prop('href')) === '[object SVGAnimatedString]') { 21116 name = 'xlinkHref'; 21117 attr.$attr[name] = 'xlink:href'; 21118 propName = null; 21119 } 21120 21121 attr.$observe(normalized, function(value) { 21122 if (!value) { 21123 if (attrName === 'href') { 21124 attr.$set(name, null); 21125 } 21126 return; 21127 } 21128 21129 attr.$set(name, value); 21130 21131 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist 21132 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need 21133 // to set the property as well to achieve the desired effect. 21134 // we use attr[attrName] value since $set can sanitize the url. 21135 if (msie && propName) element.prop(propName, attr[name]); 21136 }); 21137 } 21138 }; 21139 }; 21140}); 21141 21142/* global -nullFormCtrl */ 21143var nullFormCtrl = { 21144 $addControl: noop, 21145 $removeControl: noop, 21146 $setValidity: noop, 21147 $setDirty: noop, 21148 $setPristine: noop 21149}; 21150 21151/** 21152 * @ngdoc type 21153 * @name form.FormController 21154 * 21155 * @property {boolean} $pristine True if user has not interacted with the form yet. 21156 * @property {boolean} $dirty True if user has already interacted with the form. 21157 * @property {boolean} $valid True if all of the containing forms and controls are valid. 21158 * @property {boolean} $invalid True if at least one containing control or form is invalid. 21159 * 21160 * @property {Object} $error Is an object hash, containing references to all invalid controls or 21161 * forms, where: 21162 * 21163 * - keys are validation tokens (error names), 21164 * - values are arrays of controls or forms that are invalid for given error name. 21165 * 21166 * 21167 * Built-in validation tokens: 21168 * 21169 * - `email` 21170 * - `max` 21171 * - `maxlength` 21172 * - `min` 21173 * - `minlength` 21174 * - `number` 21175 * - `pattern` 21176 * - `required` 21177 * - `url` 21178 * 21179 * @description 21180 * `FormController` keeps track of all its controls and nested forms as well as the state of them, 21181 * such as being valid/invalid or dirty/pristine. 21182 * 21183 * Each {@link ng.directive:form form} directive creates an instance 21184 * of `FormController`. 21185 * 21186 */ 21187//asks for $scope to fool the BC controller module 21188FormController.$inject = ['$element', '$attrs', '$scope', '$animate']; 21189function FormController(element, attrs, $scope, $animate) { 21190 var form = this, 21191 parentForm = element.parent().controller('form') || nullFormCtrl, 21192 invalidCount = 0, // used to easily determine if we are valid 21193 errors = form.$error = {}, 21194 controls = []; 21195 21196 // init state 21197 form.$name = attrs.name || attrs.ngForm; 21198 form.$dirty = false; 21199 form.$pristine = true; 21200 form.$valid = true;
21201 form.$invalid = false; 21202 21203 parentForm.$addControl(form); 21204 21205 // Setup initial state of the control 21206 element.addClass(PRISTINE_CLASS); 21207 toggleValidCss(true); 21208 21209 // convenience method for easy toggling of classes 21210 function toggleValidCss(isValid, validationErrorKey) { 21211 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; 21212 $animate.setClass(element, 21213 (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey, 21214 (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); 21215 } 21216 21217 /** 21218 * @ngdoc method 21219 * @name form.FormController#$addControl 21220 * 21221 * @description 21222 * Register a control with the form. 21223 * 21224 * Input elements using ngModelController do this automatically when they are linked. 21225 */ 21226 form.$addControl = function(control) { 21227 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored 21228 // and not added to the scope. Now we throw an error. 21229 assertNotHasOwnProperty(control.$name, 'input'); 21230 controls.push(control); 21231 21232 if (control.$name) { 21233 form[control.$name] = control; 21234 } 21235 }; 21236 21237 /** 21238 * @ngdoc method 21239 * @name form.FormController#$removeControl 21240 * 21241 * @description 21242 * Deregister a control from the form. 21243 * 21244 * Input elements using ngModelController do this automatically when they are destroyed. 21245 */ 21246 form.$removeControl = function(control) { 21247 if (control.$name && form[control.$name] === control) { 21248 delete form[control.$name]; 21249 } 21250 forEach(errors, function(queue, validationToken) { 21251 form.$setValidity(validationToken, true, control); 21252 }); 21253 21254 arrayRemove(controls, control); 21255 }; 21256 21257 /** 21258 * @ngdoc method 21259 * @name form.FormController#$setValidity 21260 * 21261 * @description 21262 * Sets the validity of a form control. 21263 * 21264 * This method will also propagate to parent forms. 21265 */ 21266 form.$setValidity = function(validationToken, isValid, control) { 21267 var queue = errors[validationToken]; 21268 21269 if (isValid) { 21270 if (queue) { 21271 arrayRemove(queue, control); 21272 if (!queue.length) { 21273 invalidCount--; 21274 if (!invalidCount) { 21275 toggleValidCss(isValid); 21276 form.$valid = true; 21277 form.$invalid = false; 21278 } 21279 errors[validationToken] = false; 21280 toggleValidCss(true, validationToken); 21281 parentForm.$setValidity(validationToken, true, form); 21282 } 21283 } 21284 21285 } else { 21286 if (!invalidCount) { 21287 toggleValidCss(isValid); 21288 } 21289 if (queue) { 21290 if (includes(queue, control)) return; 21291 } else { 21292 errors[validationToken] = queue = []; 21293 invalidCount++; 21294 toggleValidCss(false, validationToken); 21295 parentForm.$setValidity(validationToken, false, form); 21296 } 21297 queue.push(control); 21298 21299 form.$valid = false; 21300 form.$invalid = true; 21301 } 21302 }; 21303 21304 /** 21305 * @ngdoc method 21306 * @name form.FormController#$setDirty 21307 * 21308 * @description 21309 * Sets the form to a dirty state. 21310 * 21311 * This method can be called to add the 'ng-dirty' class and set the form to a dirty 21312 * state (ng-dirty class). This method will also propagate to parent forms. 21313 */ 21314 form.$setDirty = function() { 21315 $animate.removeClass(element, PRISTINE_CLASS); 21316 $animate.addClass(element, DIRTY_CLASS); 21317 form.$dirty = true; 21318 form.$pristine = false; 21319 parentForm.$setDirty(); 21320 }; 21321 21322 /** 21323 * @ngdoc method 21324 * @name form.FormController#$setPristine 21325 * 21326 * @description 21327 * Sets the form to its pristine state. 21328 * 21329 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine 21330 * state (ng-pristine class). This method will also propagate to all the controls contained 21331 * in this form. 21332 * 21333 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after 21334 * saving or resetting it. 21335 */ 21336 form.$setPristine = function () { 21337 $animate.removeClass(element, DIRTY_CLASS); 21338 $animate.addClass(element, PRISTINE_CLASS); 21339 form.$dirty = false; 21340 form.$pristine = true;
21341 forEach(controls, function(control) { 21342 control.$setPristine(); 21343 }); 21344 }; 21345} 21346 21347 21348/** 21349 * @ngdoc directive 21350 * @name ngForm 21351 * @restrict EAC 21352 * 21353 * @description 21354 * Nestable alias of {@link ng.directive:form `form`} directive. HTML 21355 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a 21356 * sub-group of controls needs to be determined. 21357 * 21358 * Note: the purpose of `ngForm` is to group controls, 21359 * but not to be a replacement for the `<form>` tag with all of its capabilities 21360 * (e.g. posting to the server, ...). 21361 * 21362 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into 21363 * related scope, under this name. 21364 * 21365 */ 21366 21367 /** 21368 * @ngdoc directive 21369 * @name form 21370 * @restrict E 21371 * 21372 * @description 21373 * Directive that instantiates 21374 * {@link form.FormController FormController}. 21375 * 21376 * If the `name` attribute is specified, the form controller is published onto the current scope under 21377 * this name. 21378 * 21379 * # Alias: {@link ng.directive:ngForm `ngForm`} 21380 * 21381 * In Angular forms can be nested. This means that the outer form is valid when all of the child 21382 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so 21383 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to 21384 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when 21385 * using Angular validation directives in forms that are dynamically generated using the 21386 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` 21387 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an 21388 * `ngForm` directive and nest these in an outer `form` element. 21389 * 21390 * 21391 * # CSS classes 21392 * - `ng-valid` is set if the form is valid. 21393 * - `ng-invalid` is set if the form is invalid. 21394 * - `ng-pristine` is set if the form is pristine. 21395 * - `ng-dirty` is set if the form is dirty. 21396 * 21397 * Keep in mind that ngAnimate can detect each of these classes when added and removed. 21398 * 21399 * 21400 * # Submitting a form and preventing the default action 21401 * 21402 * Since the role of forms in client-side Angular applications is different than in classical 21403 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full 21404 * page reload that sends the data to the server. Instead some javascript logic should be triggered 21405 * to handle the form submission in an application-specific way. 21406 * 21407 * For this reason, Angular prevents the default action (form submission to the server) unless the 21408 * `<form>` element has an `action` attribute specified. 21409 * 21410 * You can use one of the following two ways to specify what javascript method should be called when 21411 * a form is submitted: 21412 * 21413 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element 21414 * - {@link ng.directive:ngClick ngClick} directive on the first 21415 * button or input field of type submit (input[type=submit]) 21416 * 21417 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} 21418 * or {@link ng.directive:ngClick ngClick} directives. 21419 * This is because of the following form submission rules in the HTML specification: 21420 * 21421 * - If a form has only one input field then hitting enter in this field triggers form submit 21422 * (`ngSubmit`) 21423 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter 21424 * doesn't trigger submit 21425 * - if a form has one or more input fields and one or more buttons or input[type=submit] then 21426 * hitting enter in any of the input fields will trigger the click handler on the *first* button or 21427 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) 21428 * 21429 * 21430 * ## Animation Hooks 21431 * 21432 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. 21433 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any 21434 * other validations that are performed within the form. Animations in ngForm are similar to how 21435 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well 21436 * as JS animations. 21437 * 21438 * The following example shows a simple way to utilize CSS transitions to style a form element
21439 * that has been rendered as invalid after it has been validated: 21440 * 21441 * <pre> 21442 * //be sure to include ngAnimate as a module to hook into more 21443 * //advanced animations 21444 * .my-form { 21445 * transition:0.5s linear all; 21446 * background: white; 21447 * } 21448 * .my-form.ng-invalid { 21449 * background: red; 21450 * color:white; 21451 * } 21452 * </pre> 21453 * 21454 * @example 21455 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample"> 21456 <file name="index.html"> 21457 <script> 21458 angular.module('formExample', []) 21459 .controller('FormController', ['$scope', function($scope) { 21460 $scope.userType = 'guest'; 21461 }]); 21462 </script> 21463 <style> 21464 .my-form { 21465 -webkit-transition:all linear 0.5s; 21466 transition:all linear 0.5s; 21467 background: transparent; 21468 } 21469 .my-form.ng-invalid { 21470 background: red; 21471 } 21472 </style> 21473 <form name="myForm" ng-controller="FormController" class="my-form"> 21474 userType: <input name="input" ng-model="userType" required> 21475 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br> 21476 <tt>userType = {{userType}}</tt><br> 21477 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br> 21478 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br> 21479 <tt>myForm.$valid = {{myForm.$valid}}</tt><br> 21480 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> 21481 </form> 21482 </file> 21483 <file name="protractor.js" type="protractor"> 21484 it('should initialize to model', function() { 21485 var userType = element(by.binding('userType')); 21486 var valid = element(by.binding('myForm.input.$valid')); 21487 21488 expect(userType.getText()).toContain('guest'); 21489 expect(valid.getText()).toContain('true'); 21490 }); 21491 21492 it('should be invalid if empty', function() { 21493 var userType = element(by.binding('userType')); 21494 var valid = element(by.binding('myForm.input.$valid')); 21495 var userInput = element(by.model('userType')); 21496 21497 userInput.clear(); 21498 userInput.sendKeys(''); 21499 21500 expect(userType.getText()).toEqual('userType ='); 21501 expect(valid.getText()).toContain('false'); 21502 }); 21503 </file> 21504 </example> 21505 * 21506 * @param {string=} name Name of the form. If specified, the form controller will be published into 21507 * related scope, under this name. 21508 */ 21509var formDirectiveFactory = function(isNgForm) { 21510 return ['$timeout', function($timeout) { 21511 var formDirective = { 21512 name: 'form', 21513 restrict: isNgForm ? 'EAC' : 'E', 21514 controller: FormController, 21515 compile: function() { 21516 return { 21517 pre: function(scope, formElement, attr, controller) { 21518 if (!attr.action) { 21519 // we can't use jq events because if a form is destroyed during submission the default 21520 // action is not prevented. see #1238 21521 // 21522 // IE 9 is not affected because it doesn't fire a submit event and try to do a full 21523 // page reload if the form was destroyed by submission of the form via a click handler 21524 // on a button in the form. Looks like an IE9 specific bug. 21525 var preventDefaultListener = function(event) { 21526 event.preventDefault 21527 ? event.preventDefault() 21528 : event.returnValue = false; // IE 21529 }; 21530 21531 addEventListenerFn(formElement[0], 'submit', preventDefaultListener); 21532 21533 // unregister the preventDefault listener so that we don't not leak memory but in a 21534 // way that will achieve the prevention of the default action. 21535 formElement.on('$destroy', function() { 21536 $timeout(function() { 21537 removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); 21538 }, 0, false); 21539 }); 21540 } 21541 21542 var parentFormCtrl = formElement.parent().controller('form'), 21543 alias = attr.name || attr.ngForm; 21544 21545 if (alias) { 21546 setter(scope, alias, controller, alias); 21547 } 21548 if (parentFormCtrl) { 21549 formElement.on('$destroy', function() { 21550 parentFormCtrl.$removeControl(controller); 21551 if (alias) { 21552 setter(scope, alias, undefined, alias); 21553 } 21554 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards 21555 }); 21556 } 21557 } 21558 }; 21559 } 21560 }; 21561 21562 return formDirective; 21563 }]; 21564}; 21565 21566var formDirective = formDirectiveFactory(); 21567var ngFormDirective = formDirectiveFactory(true); 21568 21569/* global VALID_CLASS: true, 21570 INVALID_CLASS: true, 21571 PRISTINE_CLASS: true, 21572 DIRTY_CLASS: true 21573*/ 21574 21575var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; 21576var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
21577var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; 21578 21579var inputType = { 21580 21581 /** 21582 * @ngdoc input 21583 * @name input[text] 21584 * 21585 * @description 21586 * Standard HTML text input with angular data binding, inherited by most of the `input` elements. 21587 * 21588 * *NOTE* Not every feature offered is available for all input types. 21589 * 21590 * @param {string} ngModel Assignable angular expression to data-bind to. 21591 * @param {string=} name Property name of the form under which the control is published. 21592 * @param {string=} required Adds `required` validation error key if the value is not entered. 21593 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 21594 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 21595 * `required` when you want to data-bind to the `required` attribute. 21596 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 21597 * minlength. 21598 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 21599 * maxlength. 21600 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 21601 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 21602 * patterns defined as scope expressions. 21603 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21604 * interaction with the input element. 21605 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. 21606 * This parameter is ignored for input[type=password] controls, which will never trim the 21607 * input. 21608 * 21609 * @example 21610 <example name="text-input-directive" module="textInputExample"> 21611 <file name="index.html"> 21612 <script> 21613 angular.module('textInputExample', []) 21614 .controller('ExampleController', ['$scope', function($scope) { 21615 $scope.text = 'guest'; 21616 $scope.word = /^\s*\w*\s*$/; 21617 }]); 21618 </script>
21619 <form name="myForm" ng-controller="ExampleController"> 21620 Single word: <input type="text" name="input" ng-model="text" 21621 ng-pattern="word" required ng-trim="false"> 21622 <span class="error" ng-show="myForm.input.$error.required"> 21623 Required!</span> 21624 <span class="error" ng-show="myForm.input.$error.pattern"> 21625 Single word only!</span> 21626 21627 <tt>text = {{text}}</tt><br/> 21628 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 21629 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 21630 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 21631 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 21632 </form> 21633 </file> 21634 <file name="protractor.js" type="protractor"> 21635 var text = element(by.binding('text')); 21636 var valid = element(by.binding('myForm.input.$valid')); 21637 var input = element(by.model('text')); 21638 21639 it('should initialize to model', function() { 21640 expect(text.getText()).toContain('guest'); 21641 expect(valid.getText()).toContain('true'); 21642 }); 21643 21644 it('should be invalid if empty', function() { 21645 input.clear(); 21646 input.sendKeys(''); 21647 21648 expect(text.getText()).toEqual('text ='); 21649 expect(valid.getText()).toContain('false'); 21650 }); 21651 21652 it('should be invalid if multi word', function() { 21653 input.clear(); 21654 input.sendKeys('hello world'); 21655 21656 expect(valid.getText()).toContain('false'); 21657 }); 21658 </file> 21659 </example> 21660 */ 21661 'text': textInputType, 21662 21663 21664 /** 21665 * @ngdoc input 21666 * @name input[number] 21667 * 21668 * @description 21669 * Text input with number validation and transformation. Sets the `number` validation 21670 * error if not a valid number. 21671 * 21672 * @param {string} ngModel Assignable angular expression to data-bind to. 21673 * @param {string=} name Property name of the form under which the control is published. 21674 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. 21675 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. 21676 * @param {string=} required Sets `required` validation error key if the value is not entered. 21677 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 21678 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 21679 * `required` when you want to data-bind to the `required` attribute. 21680 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 21681 * minlength. 21682 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 21683 * maxlength. 21684 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 21685 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 21686 * patterns defined as scope expressions. 21687 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21688 * interaction with the input element. 21689 * 21690 * @example 21691 <example name="number-input-directive" module="numberExample"> 21692 <file name="index.html"> 21693 <script> 21694 angular.module('numberExample', []) 21695 .controller('ExampleController', ['$scope', function($scope) { 21696 $scope.value = 12; 21697 }]); 21698 </script>
21699 <form name="myForm" ng-controller="ExampleController"> 21700 Number: <input type="number" name="input" ng-model="value" 21701 min="0" max="99" required> 21702 <span class="error" ng-show="myForm.input.$error.required"> 21703 Required!</span> 21704 <span class="error" ng-show="myForm.input.$error.number"> 21705 Not valid number!</span> 21706 <tt>value = {{value}}</tt><br/> 21707 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 21708 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 21709 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 21710 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 21711 </form> 21712 </file> 21713 <file name="protractor.js" type="protractor"> 21714 var value = element(by.binding('value')); 21715 var valid = element(by.binding('myForm.input.$valid')); 21716 var input = element(by.model('value')); 21717 21718 it('should initialize to model', function() { 21719 expect(value.getText()).toContain('12'); 21720 expect(valid.getText()).toContain('true'); 21721 }); 21722 21723 it('should be invalid if empty', function() { 21724 input.clear(); 21725 input.sendKeys(''); 21726 expect(value.getText()).toEqual('value ='); 21727 expect(valid.getText()).toContain('false'); 21728 }); 21729 21730 it('should be invalid if over max', function() { 21731 input.clear(); 21732 input.sendKeys('123'); 21733 expect(value.getText()).toEqual('value ='); 21734 expect(valid.getText()).toContain('false'); 21735 }); 21736 </file> 21737 </example> 21738 */ 21739 'number': numberInputType, 21740 21741 21742 /** 21743 * @ngdoc input 21744 * @name input[url] 21745 * 21746 * @description 21747 * Text input with URL validation. Sets the `url` validation error key if the content is not a 21748 * valid URL. 21749 * 21750 * @param {string} ngModel Assignable angular expression to data-bind to. 21751 * @param {string=} name Property name of the form under which the control is published. 21752 * @param {string=} required Sets `required` validation error key if the value is not entered. 21753 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 21754 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 21755 * `required` when you want to data-bind to the `required` attribute. 21756 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 21757 * minlength. 21758 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 21759 * maxlength. 21760 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 21761 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 21762 * patterns defined as scope expressions. 21763 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21764 * interaction with the input element. 21765 * 21766 * @example 21767 <example name="url-input-directive" module="urlExample"> 21768 <file name="index.html"> 21769 <script> 21770 angular.module('urlExample', []) 21771 .controller('ExampleController', ['$scope', function($scope) { 21772 $scope.text = 'http://google.com'; 21773 }]); 21774 </script>
21775 <form name="myForm" ng-controller="ExampleController"> 21776 URL: <input type="url" name="input" ng-model="text" required> 21777 <span class="error" ng-show="myForm.input.$error.required"> 21778 Required!</span> 21779 <span class="error" ng-show="myForm.input.$error.url"> 21780 Not valid url!</span> 21781 <tt>text = {{text}}</tt><br/> 21782 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 21783 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 21784 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 21785 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 21786 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> 21787 </form> 21788 </file> 21789 <file name="protractor.js" type="protractor"> 21790 var text = element(by.binding('text')); 21791 var valid = element(by.binding('myForm.input.$valid')); 21792 var input = element(by.model('text')); 21793 21794 it('should initialize to model', function() { 21795 expect(text.getText()).toContain('http://google.com'); 21796 expect(valid.getText()).toContain('true'); 21797 }); 21798 21799 it('should be invalid if empty', function() { 21800 input.clear(); 21801 input.sendKeys(''); 21802 21803 expect(text.getText()).toEqual('text ='); 21804 expect(valid.getText()).toContain('false'); 21805 }); 21806 21807 it('should be invalid if not url', function() { 21808 input.clear(); 21809 input.sendKeys('box'); 21810 21811 expect(valid.getText()).toContain('false'); 21812 }); 21813 </file> 21814 </example> 21815 */ 21816 'url': urlInputType, 21817 21818 21819 /** 21820 * @ngdoc input 21821 * @name input[email] 21822 * 21823 * @description 21824 * Text input with email validation. Sets the `email` validation error key if not a valid email 21825 * address. 21826 * 21827 * @param {string} ngModel Assignable angular expression to data-bind to. 21828 * @param {string=} name Property name of the form under which the control is published. 21829 * @param {string=} required Sets `required` validation error key if the value is not entered. 21830 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 21831 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 21832 * `required` when you want to data-bind to the `required` attribute. 21833 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 21834 * minlength. 21835 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 21836 * maxlength. 21837 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 21838 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 21839 * patterns defined as scope expressions. 21840 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21841 * interaction with the input element. 21842 * 21843 * @example 21844 <example name="email-input-directive" module="emailExample"> 21845 <file name="index.html"> 21846 <script> 21847 angular.module('emailExample', []) 21848 .controller('ExampleController', ['$scope', function($scope) { 21849 $scope.text = '[email protected]'; 21850 }]); 21851 </script>
21852 <form name="myForm" ng-controller="ExampleController"> 21853 Email: <input type="email" name="input" ng-model="text" required> 21854 <span class="error" ng-show="myForm.input.$error.required"> 21855 Required!</span> 21856 <span class="error" ng-show="myForm.input.$error.email"> 21857 Not valid email!</span> 21858 <tt>text = {{text}}</tt><br/> 21859 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> 21860 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> 21861 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 21862 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 21863 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> 21864 </form> 21865 </file> 21866 <file name="protractor.js" type="protractor"> 21867 var text = element(by.binding('text')); 21868 var valid = element(by.binding('myForm.input.$valid')); 21869 var input = element(by.model('text')); 21870 21871 it('should initialize to model', function() { 21872 expect(text.getText()).toContain('[email protected]'); 21873 expect(valid.getText()).toContain('true'); 21874 }); 21875 21876 it('should be invalid if empty', function() { 21877 input.clear(); 21878 input.sendKeys(''); 21879 expect(text.getText()).toEqual('text ='); 21880 expect(valid.getText()).toContain('false'); 21881 }); 21882 21883 it('should be invalid if not email', function() { 21884 input.clear(); 21885 input.sendKeys('xxx'); 21886 21887 expect(valid.getText()).toContain('false'); 21888 }); 21889 </file> 21890 </example> 21891 */ 21892 'email': emailInputType, 21893 21894 21895 /** 21896 * @ngdoc input 21897 * @name input[radio] 21898 * 21899 * @description 21900 * HTML radio button. 21901 * 21902 * @param {string} ngModel Assignable angular expression to data-bind to. 21903 * @param {string} value The value to which the expression should be set when selected. 21904 * @param {string=} name Property name of the form under which the control is published. 21905 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21906 * interaction with the input element. 21907 * @param {string} ngValue Angular expression which sets the value to which the expression should 21908 * be set when selected. 21909 * 21910 * @example 21911 <example name="radio-input-directive" module="radioExample"> 21912 <file name="index.html"> 21913 <script> 21914 angular.module('radioExample', []) 21915 .controller('ExampleController', ['$scope', function($scope) { 21916 $scope.color = 'blue'; 21917 $scope.specialValue = { 21918 "id": "12345", 21919 "value": "green" 21920 }; 21921 }]); 21922 </script>
21923 <form name="myForm" ng-controller="ExampleController"> 21924 <input type="radio" ng-model="color" value="red"> Red <br/> 21925 <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/> 21926 <input type="radio" ng-model="color" value="blue"> Blue <br/> 21927 <tt>color = {{color | json}}</tt><br/> 21928 </form> 21929 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. 21930 </file> 21931 <file name="protractor.js" type="protractor"> 21932 it('should change state', function() { 21933 var color = element(by.binding('color')); 21934 21935 expect(color.getText()).toContain('blue'); 21936 21937 element.all(by.model('color')).get(0).click(); 21938 21939 expect(color.getText()).toContain('red'); 21940 }); 21941 </file> 21942 </example> 21943 */ 21944 'radio': radioInputType, 21945 21946 21947 /** 21948 * @ngdoc input 21949 * @name input[checkbox] 21950 * 21951 * @description 21952 * HTML checkbox. 21953 * 21954 * @param {string} ngModel Assignable angular expression to data-bind to. 21955 * @param {string=} name Property name of the form under which the control is published. 21956 * @param {string=} ngTrueValue The value to which the expression should be set when selected. 21957 * @param {string=} ngFalseValue The value to which the expression should be set when not selected. 21958 * @param {string=} ngChange Angular expression to be executed when input changes due to user 21959 * interaction with the input element. 21960 * 21961 * @example 21962 <example name="checkbox-input-directive" module="checkboxExample"> 21963 <file name="index.html"> 21964 <script> 21965 angular.module('checkboxExample', []) 21966 .controller('ExampleController', ['$scope', function($scope) { 21967 $scope.value1 = true; 21968 $scope.value2 = 'YES' 21969 }]); 21970 </script>
21971 <form name="myForm" ng-controller="ExampleController"> 21972 Value1: <input type="checkbox" ng-model="value1"> <br/> 21973 Value2: <input type="checkbox" ng-model="value2" 21974 ng-true-value="YES" ng-false-value="NO"> <br/> 21975 <tt>value1 = {{value1}}</tt><br/> 21976 <tt>value2 = {{value2}}</tt><br/> 21977 </form> 21978 </file> 21979 <file name="protractor.js" type="protractor"> 21980 it('should change state', function() { 21981 var value1 = element(by.binding('value1')); 21982 var value2 = element(by.binding('value2')); 21983 21984 expect(value1.getText()).toContain('true'); 21985 expect(value2.getText()).toContain('YES'); 21986 21987 element(by.model('value1')).click(); 21988 element(by.model('value2')).click(); 21989 21990 expect(value1.getText()).toContain('false'); 21991 expect(value2.getText()).toContain('NO'); 21992 }); 21993 </file> 21994 </example> 21995 */ 21996 'checkbox': checkboxInputType, 21997 21998 'hidden': noop, 21999 'button': noop, 22000 'submit': noop, 22001 'reset': noop, 22002 'file': noop 22003}; 22004 22005// A helper function to call $setValidity and return the value / undefined, 22006// a pattern that is repeated a lot in the input validation logic. 22007function validate(ctrl, validatorName, validity, value){ 22008 ctrl.$setValidity(validatorName, validity); 22009 return validity ? value : undefined; 22010} 22011 22012function testFlags(validity, flags) { 22013 var i, flag; 22014 if (flags) { 22015 for (i=0; i<flags.length; ++i) { 22016 flag = flags[i]; 22017 if (validity[flag]) { 22018 return true; 22019 } 22020 } 22021 } 22022 return false; 22023} 22024 22025// Pass validity so that behaviour can be mocked easier. 22026function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, validity) { 22027 if (isObject(validity)) { 22028 ctrl.$$hasNativeValidators = true; 22029 var validator = function(value) { 22030 // Don't overwrite previous validation, don't consider valueMissing to apply (ng-required can 22031 // perform the required validation) 22032 if (!ctrl.$error[validatorName] && 22033 !testFlags(validity, ignoreFlags) && 22034 testFlags(validity, badFlags)) { 22035 ctrl.$setValidity(validatorName, false); 22036 return; 22037 } 22038 return value; 22039 }; 22040 ctrl.$parsers.push(validator); 22041 } 22042} 22043 22044function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { 22045 var validity = element.prop(VALIDITY_STATE_PROPERTY); 22046 var placeholder = element[0].placeholder, noevent = {}; 22047 var type = lowercase(element[0].type); 22048 ctrl.$$validityState = validity; 22049 22050 // In composition mode, users are still inputing intermediate text buffer, 22051 // hold the listener until composition is done. 22052 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent 22053 if (!$sniffer.android) { 22054 var composing = false; 22055 22056 element.on('compositionstart', function(data) { 22057 composing = true; 22058 }); 22059 22060 element.on('compositionend', function() { 22061 composing = false; 22062 listener(); 22063 }); 22064 } 22065 22066 var listener = function(ev) { 22067 if (composing) return; 22068 var value = element.val(); 22069 22070 // IE (11 and under) seem to emit an 'input' event if the placeholder value changes. 22071 // We don't want to dirty the value when this happens, so we abort here. Unfortunately, 22072 // IE also sends input events for other non-input-related things, (such as focusing on a 22073 // form control), so this change is not entirely enough to solve this. 22074 if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) { 22075 placeholder = element[0].placeholder; 22076 return; 22077 } 22078 22079 // By default we will trim the value 22080 // If the attribute ng-trim exists we will avoid trimming 22081 // If input type is 'password', the value is never trimmed 22082 if (type !== 'password' && (toBoolean(attr.ngTrim || 'T'))) { 22083 value = trim(value); 22084 } 22085 22086 // If a control is suffering from bad input, browsers discard its value, so it may be 22087 // necessary to revalidate even if the control's value is the same empty value twice in 22088 // a row. 22089 var revalidate = validity && ctrl.$$hasNativeValidators; 22090 if (ctrl.$viewValue !== value || (value === '' && revalidate)) { 22091 if (scope.$root.$$phase) { 22092 ctrl.$setViewValue(value); 22093 } else { 22094 scope.$apply(function() { 22095 ctrl.$setViewValue(value); 22096 }); 22097 } 22098 } 22099 }; 22100 22101 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the 22102 // input event on backspace, delete or cut 22103 if ($sniffer.hasEvent('input')) { 22104 element.on('input', listener); 22105 } else { 22106 var timeout; 22107 22108 var deferListener = function() { 22109 if (!timeout) { 22110 timeout = $browser.defer(function() { 22111 listener(); 22112 timeout = null; 22113 }); 22114 } 22115 }; 22116 22117 element.on('keydown', function(event) { 22118 var key = event.keyCode; 22119 22120 // ignore 22121 // command modifiers arrows 22122 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; 22123 22124 deferListener(); 22125 });
22126 22127 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it 22128 if ($sniffer.hasEvent('paste')) { 22129 element.on('paste cut', deferListener); 22130 } 22131 } 22132 22133 // if user paste into input using mouse on older browser 22134 // or form autocomplete on newer browser, we need "change" event to catch it 22135 element.on('change', listener); 22136 22137 ctrl.$render = function() { 22138 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); 22139 }; 22140 22141 // pattern validator 22142 var pattern = attr.ngPattern, 22143 patternValidator, 22144 match; 22145 22146 if (pattern) { 22147 var validateRegex = function(regexp, value) { 22148 return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value); 22149 }; 22150 match = pattern.match(/^\/(.*)\/([gim]*)$/); 22151 if (match) { 22152 pattern = new RegExp(match[1], match[2]); 22153 patternValidator = function(value) { 22154 return validateRegex(pattern, value); 22155 }; 22156 } else { 22157 patternValidator = function(value) { 22158 var patternObj = scope.$eval(pattern); 22159 22160 if (!patternObj || !patternObj.test) { 22161 throw minErr('ngPattern')('noregexp', 22162 'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern, 22163 patternObj, startingTag(element)); 22164 } 22165 return validateRegex(patternObj, value); 22166 }; 22167 } 22168 22169 ctrl.$formatters.push(patternValidator); 22170 ctrl.$parsers.push(patternValidator); 22171 } 22172 22173 // min length validator 22174 if (attr.ngMinlength) { 22175 var minlength = int(attr.ngMinlength); 22176 var minLengthValidator = function(value) { 22177 return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value); 22178 }; 22179 22180 ctrl.$parsers.push(minLengthValidator); 22181 ctrl.$formatters.push(minLengthValidator); 22182 } 22183 22184 // max length validator 22185 if (attr.ngMaxlength) { 22186 var maxlength = int(attr.ngMaxlength); 22187 var maxLengthValidator = function(value) { 22188 return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value); 22189 }; 22190 22191 ctrl.$parsers.push(maxLengthValidator); 22192 ctrl.$formatters.push(maxLengthValidator); 22193 } 22194} 22195 22196var numberBadFlags = ['badInput']; 22197 22198function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { 22199 textInputType(scope, element, attr, ctrl, $sniffer, $browser); 22200 22201 ctrl.$parsers.push(function(value) { 22202 var empty = ctrl.$isEmpty(value); 22203 if (empty || NUMBER_REGEXP.test(value)) { 22204 ctrl.$setValidity('number', true); 22205 return value === '' ? null : (empty ? value : parseFloat(value)); 22206 } else { 22207 ctrl.$setValidity('number', false); 22208 return undefined; 22209 } 22210 }); 22211 22212 addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState); 22213 22214 ctrl.$formatters.push(function(value) { 22215 return ctrl.$isEmpty(value) ? '' : '' + value; 22216 }); 22217 22218 if (attr.min) { 22219 var minValidator = function(value) { 22220 var min = parseFloat(attr.min); 22221 return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value); 22222 }; 22223 22224 ctrl.$parsers.push(minValidator); 22225 ctrl.$formatters.push(minValidator); 22226 } 22227 22228 if (attr.max) { 22229 var maxValidator = function(value) { 22230 var max = parseFloat(attr.max); 22231 return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value); 22232 }; 22233 22234 ctrl.$parsers.push(maxValidator); 22235 ctrl.$formatters.push(maxValidator); 22236 } 22237 22238 ctrl.$formatters.push(function(value) { 22239 return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value); 22240 }); 22241} 22242 22243function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { 22244 textInputType(scope, element, attr, ctrl, $sniffer, $browser); 22245 22246 var urlValidator = function(value) { 22247 return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value); 22248 }; 22249 22250 ctrl.$formatters.push(urlValidator); 22251 ctrl.$parsers.push(urlValidator); 22252} 22253 22254function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { 22255 textInputType(scope, element, attr, ctrl, $sniffer, $browser); 22256 22257 var emailValidator = function(value) { 22258 return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value); 22259 }; 22260 22261 ctrl.$formatters.push(emailValidator);
22262 ctrl.$parsers.push(emailValidator); 22263} 22264 22265function radioInputType(scope, element, attr, ctrl) { 22266 // make the name unique, if not defined 22267 if (isUndefined(attr.name)) { 22268 element.attr('name', nextUid()); 22269 } 22270 22271 element.on('click', function() { 22272 if (element[0].checked) { 22273 scope.$apply(function() { 22274 ctrl.$setViewValue(attr.value); 22275 }); 22276 } 22277 }); 22278 22279 ctrl.$render = function() { 22280 var value = attr.value; 22281 element[0].checked = (value == ctrl.$viewValue); 22282 }; 22283 22284 attr.$observe('value', ctrl.$render); 22285} 22286 22287function checkboxInputType(scope, element, attr, ctrl) { 22288 var trueValue = attr.ngTrueValue, 22289 falseValue = attr.ngFalseValue; 22290 22291 if (!isString(trueValue)) trueValue = true; 22292 if (!isString(falseValue)) falseValue = false; 22293 22294 element.on('click', function() { 22295 scope.$apply(function() { 22296 ctrl.$setViewValue(element[0].checked); 22297 }); 22298 }); 22299 22300 ctrl.$render = function() { 22301 element[0].checked = ctrl.$viewValue; 22302 }; 22303 22304 // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox. 22305 ctrl.$isEmpty = function(value) { 22306 return value !== trueValue; 22307 }; 22308 22309 ctrl.$formatters.push(function(value) { 22310 return value === trueValue; 22311 }); 22312 22313 ctrl.$parsers.push(function(value) { 22314 return value ? trueValue : falseValue; 22315 }); 22316} 22317 22318 22319/** 22320 * @ngdoc directive 22321 * @name textarea 22322 * @restrict E 22323 * 22324 * @description 22325 * HTML textarea element control with angular data-binding. The data-binding and validation 22326 * properties of this element are exactly the same as those of the 22327 * {@link ng.directive:input input element}. 22328 * 22329 * @param {string} ngModel Assignable angular expression to data-bind to. 22330 * @param {string=} name Property name of the form under which the control is published. 22331 * @param {string=} required Sets `required` validation error key if the value is not entered. 22332 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 22333 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 22334 * `required` when you want to data-bind to the `required` attribute. 22335 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 22336 * minlength. 22337 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 22338 * maxlength. 22339 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 22340 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 22341 * patterns defined as scope expressions. 22342 * @param {string=} ngChange Angular expression to be executed when input changes due to user 22343 * interaction with the input element. 22344 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. 22345 */ 22346 22347 22348/** 22349 * @ngdoc directive 22350 * @name input 22351 * @restrict E 22352 * 22353 * @description 22354 * HTML input element control with angular data-binding. Input control follows HTML5 input types 22355 * and polyfills the HTML5 validation behavior for older browsers. 22356 * 22357 * *NOTE* Not every feature offered is available for all input types. 22358 * 22359 * @param {string} ngModel Assignable angular expression to data-bind to. 22360 * @param {string=} name Property name of the form under which the control is published. 22361 * @param {string=} required Sets `required` validation error key if the value is not entered. 22362 * @param {boolean=} ngRequired Sets `required` attribute if set to true 22363 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than 22364 * minlength. 22365 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than 22366 * maxlength. 22367 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the 22368 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for 22369 * patterns defined as scope expressions. 22370 * @param {string=} ngChange Angular expression to be executed when input changes due to user 22371 * interaction with the input element. 22372 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. 22373 * This parameter is ignored for input[type=password] controls, which will never trim the 22374 * input. 22375 * 22376 * @example 22377 <example name="input-directive" module="inputExample"> 22378 <file name="index.html"> 22379 <script> 22380 angular.module('inputExample', [])
22381 .controller('ExampleController', ['$scope', function($scope) { 22382 $scope.user = {name: 'guest', last: 'visitor'}; 22383 }]); 22384 </script> 22385 <div ng-controller="ExampleController"> 22386 <form name="myForm"> 22387 User name: <input type="text" name="userName" ng-model="user.name" required> 22388 <span class="error" ng-show="myForm.userName.$error.required"> 22389 Required!</span><br> 22390 Last name: <input type="text" name="lastName" ng-model="user.last" 22391 ng-minlength="3" ng-maxlength="10"> 22392 <span class="error" ng-show="myForm.lastName.$error.minlength"> 22393 Too short!</span> 22394 <span class="error" ng-show="myForm.lastName.$error.maxlength"> 22395 Too long!</span><br> 22396 </form> 22397 <hr> 22398 <tt>user = {{user}}</tt><br/> 22399 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br> 22400 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br> 22401 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br> 22402 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br> 22403 <tt>myForm.$valid = {{myForm.$valid}}</tt><br> 22404 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> 22405 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br> 22406 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br> 22407 </div> 22408 </file> 22409 <file name="protractor.js" type="protractor"> 22410 var user = element(by.binding('{{user}}')); 22411 var userNameValid = element(by.binding('myForm.userName.$valid')); 22412 var lastNameValid = element(by.binding('myForm.lastName.$valid')); 22413 var lastNameError = element(by.binding('myForm.lastName.$error')); 22414 var formValid = element(by.binding('myForm.$valid')); 22415 var userNameInput = element(by.model('user.name')); 22416 var userLastInput = element(by.model('user.last')); 22417 22418 it('should initialize to model', function() {
22419 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); 22420 expect(userNameValid.getText()).toContain('true'); 22421 expect(formValid.getText()).toContain('true'); 22422 }); 22423 22424 it('should be invalid if empty when required', function() { 22425 userNameInput.clear(); 22426 userNameInput.sendKeys(''); 22427 22428 expect(user.getText()).toContain('{"last":"visitor"}'); 22429 expect(userNameValid.getText()).toContain('false'); 22430 expect(formValid.getText()).toContain('false'); 22431 }); 22432 22433 it('should be valid if empty when min length is set', function() { 22434 userLastInput.clear(); 22435 userLastInput.sendKeys(''); 22436 22437 expect(user.getText()).toContain('{"name":"guest","last":""}'); 22438 expect(lastNameValid.getText()).toContain('true'); 22439 expect(formValid.getText()).toContain('true'); 22440 }); 22441 22442 it('should be invalid if less than required min length', function() { 22443 userLastInput.clear(); 22444 userLastInput.sendKeys('xx'); 22445
22446 expect(user.getText()).toContain('{"name":"guest"}'); 22447 expect(lastNameValid.getText()).toContain('false'); 22448 expect(lastNameError.getText()).toContain('minlength'); 22449 expect(formValid.getText()).toContain('false'); 22450 }); 22451 22452 it('should be invalid if longer than max length', function() { 22453 userLastInput.clear(); 22454 userLastInput.sendKeys('some ridiculously long name'); 22455 22456 expect(user.getText()).toContain('{"name":"guest"}'); 22457 expect(lastNameValid.getText()).toContain('false'); 22458 expect(lastNameError.getText()).toContain('maxlength'); 22459 expect(formValid.getText()).toContain('false'); 22460 }); 22461 </file> 22462 </example> 22463 */ 22464var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { 22465 return { 22466 restrict: 'E', 22467 require: '?ngModel', 22468 link: function(scope, element, attr, ctrl) { 22469 if (ctrl) { 22470 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, 22471 $browser); 22472 } 22473 } 22474 }; 22475}]; 22476 22477var VALID_CLASS = 'ng-valid', 22478 INVALID_CLASS = 'ng-invalid', 22479 PRISTINE_CLASS = 'ng-pristine', 22480 DIRTY_CLASS = 'ng-dirty'; 22481 22482/** 22483 * @ngdoc type 22484 * @name ngModel.NgModelController 22485 * 22486 * @property {string} $viewValue Actual string value in the view. 22487 * @property {*} $modelValue The value in the model, that the control is bound to. 22488 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever 22489 the control reads value from the DOM. Each function is called, in turn, passing the value 22490 through to the next. The last return value is used to populate the model. 22491 Used to sanitize / convert the value as well as validation. For validation, 22492 the parsers should update the validity state using 22493 {@link ngModel.NgModelController#$setValidity $setValidity()}, 22494 and return `undefined` for invalid values. 22495 22496 * 22497 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever 22498 the model value changes. Each function is called, in turn, passing the value through to the 22499 next. Used to format / convert values for display in the control and validation. 22500 * ```js 22501 * function formatter(value) { 22502 * if (value) { 22503 * return value.toUpperCase(); 22504 * } 22505 * } 22506 * ngModel.$formatters.push(formatter); 22507 * ``` 22508 * 22509 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the 22510 * view value has changed. It is called with no arguments, and its return value is ignored. 22511 * This can be used in place of additional $watches against the model value. 22512 * 22513 * @property {Object} $error An object hash with all errors as keys. 22514 * 22515 * @property {boolean} $pristine True if user has not interacted with the control yet. 22516 * @property {boolean} $dirty True if user has already interacted with the control. 22517 * @property {boolean} $valid True if there is no error. 22518 * @property {boolean} $invalid True if at least one error on the control. 22519 * 22520 * @description 22521 * 22522 * `NgModelController` provides API for the `ng-model` directive. The controller contains 22523 * services for data-binding, validation, CSS updates, and value formatting and parsing. It 22524 * purposefully does not contain any logic which deals with DOM rendering or listening to 22525 * DOM events. Such DOM related logic should be provided by other directives which make use of 22526 * `NgModelController` for data-binding. 22527 * 22528 * ## Custom Control Example 22529 * This example shows how to use `NgModelController` with a custom control to achieve 22530 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) 22531 * collaborate together to achieve the desired result. 22532 * 22533 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element 22534 * contents be edited in place by the user. This will not work on older browsers. 22535 * 22536 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} 22537 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). 22538 * However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks 22539 * that content using the `$sce` service. 22540 * 22541 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> 22542 <file name="style.css"> 22543 [contenteditable] { 22544 border: 1px solid black; 22545 background-color: white; 22546 min-height: 20px; 22547 } 22548 22549 .ng-invalid { 22550 border: 1px solid red; 22551 } 22552 22553 </file> 22554 <file name="script.js">
22555 angular.module('customControl', ['ngSanitize']). 22556 directive('contenteditable', ['$sce', function($sce) { 22557 return { 22558 restrict: 'A', // only activate on element attribute 22559 require: '?ngModel', // get a hold of NgModelController 22560 link: function(scope, element, attrs, ngModel) { 22561 if(!ngModel) return; // do nothing if no ng-model 22562 22563 // Specify how UI should be updated 22564 ngModel.$render = function() { 22565 element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); 22566 }; 22567 22568 // Listen for change events to enable binding 22569 element.on('blur keyup change', function() { 22570 scope.$apply(read); 22571 }); 22572 read(); // initialize 22573 22574 // Write data to the model 22575 function read() { 22576 var html = element.html(); 22577 // When we clear the content editable the browser leaves a <br> behind 22578 // If strip-br attribute is provided then we strip this out 22579 if( attrs.stripBr && html == '<br>' ) { 22580 html = ''; 22581 } 22582 ngModel.$setViewValue(html); 22583 } 22584 } 22585 }; 22586 }]); 22587 </file> 22588 <file name="index.html"> 22589 <form name="myForm"> 22590 <div contenteditable 22591 name="myWidget" ng-model="userContent" 22592 strip-br="true" 22593 required>Change me!</div> 22594 <span ng-show="myForm.myWidget.$error.required">Required!</span> 22595 <hr> 22596 <textarea ng-model="userContent"></textarea> 22597 </form> 22598 </file> 22599 <file name="protractor.js" type="protractor"> 22600 it('should data-bind and become invalid', function() { 22601 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') { 22602 // SafariDriver can't handle contenteditable 22603 // and Firefox driver can't clear contenteditables very well 22604 return; 22605 } 22606 var contentEditable = element(by.css('[contenteditable]')); 22607 var content = 'Change me!'; 22608 22609 expect(contentEditable.getText()).toEqual(content); 22610 22611 contentEditable.clear(); 22612 contentEditable.sendKeys(protractor.Key.BACK_SPACE); 22613 expect(contentEditable.getText()).toEqual(''); 22614 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); 22615 }); 22616 </file> 22617 * </example> 22618 * 22619 * 22620 */ 22621var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', 22622 function($scope, $exceptionHandler, $attr, $element, $parse, $animate) { 22623 this.$viewValue = Number.NaN; 22624 this.$modelValue = Number.NaN; 22625 this.$parsers = []; 22626 this.$formatters = []; 22627 this.$viewChangeListeners = []; 22628 this.$pristine = true; 22629 this.$dirty = false; 22630 this.$valid = true; 22631 this.$invalid = false; 22632 this.$name = $attr.name; 22633 22634 var ngModelGet = $parse($attr.ngModel), 22635 ngModelSet = ngModelGet.assign; 22636 22637 if (!ngModelSet) { 22638 throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}", 22639 $attr.ngModel, startingTag($element)); 22640 } 22641 22642 /** 22643 * @ngdoc method 22644 * @name ngModel.NgModelController#$render 22645 * 22646 * @description 22647 * Called when the view needs to be updated. It is expected that the user of the ng-model 22648 * directive will implement this method. 22649 */ 22650 this.$render = noop; 22651 22652 /** 22653 * @ngdoc method 22654 * @name ngModel.NgModelController#$isEmpty 22655 * 22656 * @description 22657 * This is called when we need to determine if the value of the input is empty. 22658 * 22659 * For instance, the required directive does this to work out if the input has data or not. 22660 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. 22661 * 22662 * You can override this for input directives whose concept of being empty is different to the 22663 * default. The `checkboxInputType` directive does this because in its case a value of `false` 22664 * implies empty. 22665 * 22666 * @param {*} value Reference to check. 22667 * @returns {boolean} True if `value` is empty. 22668 */ 22669 this.$isEmpty = function(value) { 22670 return isUndefined(value) || value === '' || value === null || value !== value; 22671 }; 22672 22673 var parentForm = $element.inheritedData('$formController') || nullFormCtrl, 22674 invalidCount = 0, // used to easily determine if we are valid 22675 $error = this.$error = {};
22675 // keep invalid keys here 22676 22677 22678 // Setup initial state of the control 22679 $element.addClass(PRISTINE_CLASS); 22680 toggleValidCss(true); 22681 22682 // convenience method for easy toggling of classes 22683 function toggleValidCss(isValid, validationErrorKey) { 22684 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; 22685 $animate.removeClass($element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); 22686 $animate.addClass($element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); 22687 } 22688 22689 /** 22690 * @ngdoc method 22691 * @name ngModel.NgModelController#$setValidity 22692 * 22693 * @description 22694 * Change the validity state, and notifies the form when the control changes validity. (i.e. it 22695 * does not notify form if given validator is already marked as invalid). 22696 * 22697 * This method should be called by validators - i.e. the parser or formatter functions. 22698 * 22699 * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign 22700 * to `$error[validationErrorKey]=!isValid` so that it is available for data-binding. 22701 * The `validationErrorKey` should be in camelCase and will get converted into dash-case 22702 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` 22703 * class and can be bound to as `{{someForm.someControl.$error.myError}}` . 22704 * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). 22705 */ 22706 this.$setValidity = function(validationErrorKey, isValid) { 22707 // Purposeful use of ! here to cast isValid to boolean in case it is undefined 22708 // jshint -W018 22709 if ($error[validationErrorKey] === !isValid) return; 22710 // jshint +W018 22711 22712 if (isValid) { 22713 if ($error[validationErrorKey]) invalidCount--; 22714 if (!invalidCount) { 22715 toggleValidCss(true); 22716 this.$valid = true; 22717 this.$invalid = false; 22718 } 22719 } else { 22720 toggleValidCss(false); 22721 this.$invalid = true; 22722 this.$valid = false; 22723 invalidCount++; 22724 } 22725 22726 $error[validationErrorKey] = !isValid; 22727 toggleValidCss(isValid, validationErrorKey); 22728 22729 parentForm.$setValidity(validationErrorKey, isValid, this); 22730 }; 22731 22732 /** 22733 * @ngdoc method 22734 * @name ngModel.NgModelController#$setPristine 22735 * 22736 * @description 22737 * Sets the control to its pristine state. 22738 * 22739 * This method can be called to remove the 'ng-dirty' class and set the control to its pristine 22740 * state (ng-pristine class). 22741 */ 22742 this.$setPristine = function () { 22743 this.$dirty = false; 22744 this.$pristine = true; 22745 $animate.removeClass($element, DIRTY_CLASS); 22746 $animate.addClass($element, PRISTINE_CLASS); 22747 }; 22748 22749 /** 22750 * @ngdoc method 22751 * @name ngModel.NgModelController#$setViewValue 22752 * 22753 * @description 22754 * Update the view value. 22755 * 22756 * This method should be called when the view value changes, typically from within a DOM event handler. 22757 * For example {@link ng.directive:input input} and 22758 * {@link ng.directive:select select} directives call it. 22759 * 22760 * It will update the $viewValue, then pass this value through each of the functions in `$parsers`, 22761 * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to 22762 * `$modelValue` and the **expression** specified in the `ng-model` attribute. 22763 * 22764 * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called. 22765 * 22766 * Note that calling this function does not trigger a `$digest`. 22767 * 22768 * @param {string} value Value from the view. 22769 */ 22770 this.$setViewValue = function(value) { 22771 this.$viewValue = value; 22772 22773 // change to dirty 22774 if (this.$pristine) { 22775 this.$dirty = true; 22776 this.$pristine = false; 22777 $animate.removeClass($element, PRISTINE_CLASS); 22778 $animate.addClass($element, DIRTY_CLASS); 22779 parentForm.$setDirty(); 22780 } 22781 22782 forEach(this.$parsers, function(fn) { 22783 value = fn(value); 22784 }); 22785 22786 if (this.$modelValue !== value) { 22787 this.$modelValue = value; 22788 ngModelSet($scope, value);
22789 forEach(this.$viewChangeListeners, function(listener) { 22790 try { 22791 listener(); 22792 } catch(e) { 22793 $exceptionHandler(e); 22794 } 22795 }); 22796 } 22797 }; 22798 22799 // model -> value 22800 var ctrl = this; 22801 22802 $scope.$watch(function ngModelWatch() { 22803 var value = ngModelGet($scope); 22804 22805 // if scope model value and ngModel value are out of sync 22806 if (ctrl.$modelValue !== value) { 22807 22808 var formatters = ctrl.$formatters, 22809 idx = formatters.length; 22810 22811 ctrl.$modelValue = value; 22812 while(idx--) { 22813 value = formatters[idx](value); 22814 } 22815 22816 if (ctrl.$viewValue !== value) { 22817 ctrl.$viewValue = value; 22818 ctrl.$render(); 22819 } 22820 } 22821 22822 return value; 22823 }); 22824}]; 22825 22826 22827/** 22828 * @ngdoc directive 22829 * @name ngModel 22830 * 22831 * @element input 22832 * 22833 * @description 22834 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a 22835 * property on the scope using {@link ngModel.NgModelController NgModelController}, 22836 * which is created and exposed by this directive. 22837 * 22838 * `ngModel` is responsible for: 22839 * 22840 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` 22841 * require. 22842 * - Providing validation behavior (i.e. required, number, email, url). 22843 * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors). 22844 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`) including animations. 22845 * - Registering the control with its parent {@link ng.directive:form form}. 22846 * 22847 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the 22848 * current scope. If the property doesn't already exist on this scope, it will be created 22849 * implicitly and added to the scope. 22850 * 22851 * For best practices on using `ngModel`, see: 22852 * 22853 * - [https://github.com/angular/angular.js/wiki/Understanding-Scopes] 22854 * 22855 * For basic examples, how to use `ngModel`, see: 22856 * 22857 * - {@link ng.directive:input input} 22858 * - {@link input[text] text} 22859 * - {@link input[checkbox] checkbox} 22860 * - {@link input[radio] radio} 22861 * - {@link input[number] number} 22862 * - {@link input[email] email} 22863 * - {@link input[url] url} 22864 * - {@link ng.directive:select select} 22865 * - {@link ng.directive:textarea textarea} 22866 * 22867 * # CSS classes 22868 * The following CSS classes are added and removed on the associated input/select/textarea element 22869 * depending on the validity of the model. 22870 * 22871 * - `ng-valid` is set if the model is valid. 22872 * - `ng-invalid` is set if the model is invalid. 22873 * - `ng-pristine` is set if the model is pristine. 22874 * - `ng-dirty` is set if the model is dirty. 22875 * 22876 * Keep in mind that ngAnimate can detect each of these classes when added and removed. 22877 * 22878 * ## Animation Hooks 22879 * 22880 * Animations within models are triggered when any of the associated CSS classes are added and removed 22881 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`, 22882 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. 22883 * The animations that are triggered within ngModel are similar to how they work in ngClass and 22884 * animations can be hooked into using CSS transitions, keyframes as well as JS animations. 22885 * 22886 * The following example shows a simple way to utilize CSS transitions to style an input element 22887 * that has been rendered as invalid after it has been validated: 22888 * 22889 * <pre> 22890 * //be sure to include ngAnimate as a module to hook into more 22891 * //advanced animations 22892 * .my-input { 22893 * transition:0.5s linear all; 22894 * background: white; 22895 * } 22896 * .my-input.ng-invalid { 22897 * background: red; 22898 * color:white; 22899 * } 22900 * </pre> 22901 * 22902 * @example 22903 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample"> 22904 <file name="index.html"> 22905 <script> 22906 angular.module('inputExample', []) 22907 .controller('ExampleController', ['$scope', function($scope) { 22908 $scope.val = '1'; 22909 }]); 22910 </script> 22911 <style> 22912 .my-input { 22913 -webkit-transition:all linear 0.5s; 22914 transition:all linear 0.5s; 22915 background: transparent; 22916 } 22917 .my-input.ng-invalid { 22918 color:white; 22919 background: red; 22920 } 22921 </style> 22922 Update input to see transitions when valid/invalid. 22923 Integer is a valid value.
22924 <form name="testForm" ng-controller="ExampleController"> 22925 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" /> 22926 </form> 22927 </file> 22928 * </example> 22929 */ 22930var ngModelDirective = function() { 22931 return { 22932 require: ['ngModel', '^?form'], 22933 controller: NgModelController, 22934 link: function(scope, element, attr, ctrls) { 22935 // notify others, especially parent forms 22936 22937 var modelCtrl = ctrls[0], 22938 formCtrl = ctrls[1] || nullFormCtrl; 22939 22940 formCtrl.$addControl(modelCtrl); 22941 22942 scope.$on('$destroy', function() { 22943 formCtrl.$removeControl(modelCtrl); 22944 }); 22945 } 22946 }; 22947}; 22948 22949 22950/** 22951 * @ngdoc directive 22952 * @name ngChange 22953 * 22954 * @description 22955 * Evaluate the given expression when the user changes the input. 22956 * The expression is evaluated immediately, unlike the JavaScript onchange event 22957 * which only triggers at the end of a change (usually, when the user leaves the 22958 * form element or presses the return key). 22959 * The expression is not evaluated when the value change is coming from the model. 22960 * 22961 * Note, this directive requires `ngModel` to be present. 22962 * 22963 * @element input 22964 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change 22965 * in input value. 22966 * 22967 * @example 22968 * <example name="ngChange-directive" module="changeExample"> 22969 * <file name="index.html"> 22970 * <script> 22971 * angular.module('changeExample', []) 22972 * .controller('ExampleController', ['$scope', function($scope) { 22973 * $scope.counter = 0; 22974 * $scope.change = function() { 22975 * $scope.counter++; 22976 * }; 22977 * }]); 22978 * </script>
22979 * <div ng-controller="ExampleController"> 22980 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> 22981 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> 22982 * <label for="ng-change-example2">Confirmed</label><br /> 22983 * <tt>debug = {{confirmed}}</tt><br/> 22984 * <tt>counter = {{counter}}</tt><br/> 22985 * </div> 22986 * </file> 22987 * <file name="protractor.js" type="protractor"> 22988 * var counter = element(by.binding('counter')); 22989 * var debug = element(by.binding('confirmed')); 22990 * 22991 * it('should evaluate the expression if changing from view', function() { 22992 * expect(counter.getText()).toContain('0'); 22993 * 22994 * element(by.id('ng-change-example1')).click(); 22995 * 22996 * expect(counter.getText()).toContain('1'); 22997 * expect(debug.getText()).toContain('true'); 22998 * }); 22999 * 23000 * it('should not evaluate the expression if changing from model', function() { 23001 * element(by.id('ng-change-example2')).click(); 23002 23003 * expect(counter.getText()).toContain('0'); 23004 * expect(debug.getText()).toContain('true'); 23005 * }); 23006 * </file> 23007 * </example> 23008 */ 23009var ngChangeDirective = valueFn({ 23010 require: 'ngModel', 23011 link: function(scope, element, attr, ctrl) { 23012 ctrl.$viewChangeListeners.push(function() { 23013 scope.$eval(attr.ngChange); 23014 }); 23015 } 23016}); 23017 23018 23019var requiredDirective = function() { 23020 return { 23021 require: '?ngModel', 23022 link: function(scope, elm, attr, ctrl) { 23023 if (!ctrl) return; 23024 attr.required = true; // force truthy in case we are on non input element 23025 23026 var validator = function(value) { 23027 if (attr.required && ctrl.$isEmpty(value)) { 23028 ctrl.$setValidity('required', false); 23029 return; 23030 } else { 23031 ctrl.$setValidity('required', true); 23032 return value; 23033 } 23034 }; 23035 23036 ctrl.$formatters.push(validator); 23037 ctrl.$parsers.unshift(validator); 23038 23039 attr.$observe('required', function() { 23040 validator(ctrl.$viewValue); 23041 }); 23042 } 23043 }; 23044}; 23045 23046 23047/** 23048 * @ngdoc directive 23049 * @name ngList 23050 * 23051 * @description 23052 * Text input that converts between a delimited string and an array of strings. The delimiter 23053 * can be a fixed string (by default a comma) or a regular expression. 23054 * 23055 * @element input 23056 * @param {string=} ngList optional delimiter that should be used to split the value. If 23057 * specified in form `/something/` then the value will be converted into a regular expression. 23058 * 23059 * @example 23060 <example name="ngList-directive" module="listExample"> 23061 <file name="index.html"> 23062 <script> 23063 angular.module('listExample', []) 23064 .controller('ExampleController', ['$scope', function($scope) { 23065 $scope.names = ['igor', 'misko', 'vojta']; 23066 }]); 23067 </script>
23068 <form name="myForm" ng-controller="ExampleController"> 23069 List: <input name="namesInput" ng-model="names" ng-list required> 23070 <span class="error" ng-show="myForm.namesInput.$error.required"> 23071 Required!</span> 23072 <br> 23073 <tt>names = {{names}}</tt><br/> 23074 <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> 23075 <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> 23076 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> 23077 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> 23078 </form> 23079 </file> 23080 <file name="protractor.js" type="protractor"> 23081 var listInput = element(by.model('names')); 23082 var names = element(by.binding('{{names}}')); 23083 var valid = element(by.binding('myForm.namesInput.$valid')); 23084 var error = element(by.css('span.error')); 23085 23086 it('should initialize to model', function() { 23087 expect(names.getText()).toContain('["igor","misko","vojta"]'); 23088 expect(valid.getText()).toContain('true'); 23089 expect(error.getCssValue('display')).toBe('none'); 23090 }); 23091 23092 it('should be invalid if empty', function() { 23093 listInput.clear(); 23094 listInput.sendKeys(''); 23095 23096 expect(names.getText()).toContain(''); 23097 expect(valid.getText()).toContain('false'); 23098 expect(error.getCssValue('display')).not.toBe('none'); }); 23099 </file> 23100 </example> 23101 */ 23102var ngListDirective = function() { 23103 return { 23104 require: 'ngModel', 23105 link: function(scope, element, attr, ctrl) { 23106 var match = /\/(.*)\//.exec(attr.ngList), 23107 separator = match && new RegExp(match[1]) || attr.ngList || ','; 23108 23109 var parse = function(viewValue) { 23110 // If the viewValue is invalid (say required but empty) it will be `undefined` 23111 if (isUndefined(viewValue)) return; 23112 23113 var list = []; 23114 23115 if (viewValue) { 23116 forEach(viewValue.split(separator), function(value) { 23117 if (value) list.push(trim(value)); 23118 }); 23119 } 23120 23121 return list; 23122 }; 23123 23124 ctrl.$parsers.push(parse); 23125 ctrl.$formatters.push(function(value) { 23126 if (isArray(value)) { 23127 return value.join(', '); 23128 } 23129 23130 return undefined; 23131 }); 23132 23133 // Override the standard $isEmpty because an empty array means the input is empty. 23134 ctrl.$isEmpty = function(value) { 23135 return !value || !value.length; 23136 }; 23137 } 23138 }; 23139}; 23140 23141 23142var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; 23143/** 23144 * @ngdoc directive 23145 * @name ngValue 23146 * 23147 * @description 23148 * Binds the given expression to the value of `input[select]` or `input[radio]`, so 23149 * that when the element is selected, the `ngModel` of that element is set to the 23150 * bound value. 23151 * 23152 * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as 23153 * shown below. 23154 * 23155 * @element input 23156 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute 23157 * of the `input` element 23158 * 23159 * @example 23160 <example name="ngValue-directive" module="valueExample"> 23161 <file name="index.html"> 23162 <script> 23163 angular.module('valueExample', []) 23164 .controller('ExampleController', ['$scope', function($scope) { 23165 $scope.names = ['pizza', 'unicorns', 'robots']; 23166 $scope.my = { favorite: 'unicorns' }; 23167 }]); 23168 </script>
23169 <form ng-controller="ExampleController"> 23170 <h2>Which is your favorite?</h2> 23171 <label ng-repeat="name in names" for="{{name}}"> 23172 {{name}} 23173 <input type="radio" 23174 ng-model="my.favorite" 23175 ng-value="name" 23176 id="{{name}}" 23177 name="favorite"> 23178 </label> 23179 <div>You chose {{my.favorite}}</div> 23180 </form> 23181 </file> 23182 <file name="protractor.js" type="protractor"> 23183 var favorite = element(by.binding('my.favorite')); 23184 23185 it('should initialize to model', function() { 23186 expect(favorite.getText()).toContain('unicorns'); 23187 }); 23188 it('should bind the values to the inputs', function() { 23189 element.all(by.model('my.favorite')).get(0).click(); 23190 expect(favorite.getText()).toContain('pizza'); 23191 }); 23192 </file> 23193 </example> 23194 */ 23195var ngValueDirective = function() { 23196 return { 23197 priority: 100, 23198 compile: function(tpl, tplAttr) { 23199 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { 23200 return function ngValueConstantLink(scope, elm, attr) { 23201 attr.$set('value', scope.$eval(attr.ngValue)); 23202 }; 23203 } else { 23204 return function ngValueLink(scope, elm, attr) { 23205 scope.$watch(attr.ngValue, function valueWatchAction(value) { 23206 attr.$set('value', value); 23207 }); 23208 }; 23209 } 23210 } 23211 }; 23212}; 23213 23214/** 23215 * @ngdoc directive 23216 * @name ngBind 23217 * @restrict AC 23218 * 23219 * @description 23220 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element 23221 * with the value of a given expression, and to update the text content when the value of that 23222 * expression changes. 23223 * 23224 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like 23225 * `{{ expression }}` which is similar but less verbose. 23226 * 23227 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily 23228 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an 23229 * element attribute, it makes the bindings invisible to the user while the page is loading. 23230 * 23231 * An alternative solution to this problem would be using the 23232 * {@link ng.directive:ngCloak ngCloak} directive. 23233 * 23234 * 23235 * @element ANY 23236 * @param {expression} ngBind {@link guide/expression Expression} to evaluate. 23237 * 23238 * @example 23239 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. 23240 <example module="bindExample"> 23241 <file name="index.html"> 23242 <script> 23243 angular.module('bindExample', []) 23244 .controller('ExampleController', ['$scope', function($scope) { 23245 $scope.name = 'Whirled'; 23246 }]); 23247 </script>
23248 <div ng-controller="ExampleController"> 23249 Enter name: <input type="text" ng-model="name"><br> 23250 Hello <span ng-bind="name"></span>! 23251 </div> 23252 </file> 23253 <file name="protractor.js" type="protractor"> 23254 it('should check ng-bind', function() { 23255 var nameInput = element(by.model('name')); 23256 23257 expect(element(by.binding('name')).getText()).toBe('Whirled'); 23258 nameInput.clear(); 23259 nameInput.sendKeys('world'); 23260 expect(element(by.binding('name')).getText()).toBe('world'); 23261 }); 23262 </file> 23263 </example> 23264 */ 23265var ngBindDirective = ngDirective({ 23266 compile: function(templateElement) { 23267 templateElement.addClass('ng-binding'); 23268 return function (scope, element, attr) { 23269 element.data('$binding', attr.ngBind); 23270 scope.$watch(attr.ngBind, function ngBindWatchAction(value) { 23271 // We are purposefully using == here rather than === because we want to 23272 // catch when value is "null or undefined" 23273 // jshint -W041 23274 element.text(value == undefined ? '' : value); 23275 }); 23276 }; 23277 } 23278}); 23279 23280 23281/** 23282 * @ngdoc directive 23283 * @name ngBindTemplate 23284 * 23285 * @description 23286 * The `ngBindTemplate` directive specifies that the element 23287 * text content should be replaced with the interpolation of the template 23288 * in the `ngBindTemplate` attribute. 23289 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` 23290 * expressions. This directive is needed since some HTML elements 23291 * (such as TITLE and OPTION) cannot contain SPAN elements. 23292 * 23293 * @element ANY 23294 * @param {string} ngBindTemplate template of form 23295 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. 23296 * 23297 * @example 23298 * Try it here: enter text in text box and watch the greeting change. 23299 <example module="bindExample"> 23300 <file name="index.html"> 23301 <script> 23302 angular.module('bindExample', []) 23303 .controller('ExampleController', ['$scope', function ($scope) { 23304 $scope.salutation = 'Hello'; 23305 $scope.name = 'World'; 23306 }]); 23307 </script>
23308 <div ng-controller="ExampleController"> 23309 Salutation: <input type="text" ng-model="salutation"><br> 23310 Name: <input type="text" ng-model="name"><br> 23311 <pre ng-bind-template="{{salutation}} {{name}}!"></pre> 23312 </div> 23313 </file> 23314 <file name="protractor.js" type="protractor"> 23315 it('should check ng-bind', function() { 23316 var salutationElem = element(by.binding('salutation')); 23317 var salutationInput = element(by.model('salutation')); 23318 var nameInput = element(by.model('name')); 23319 23320 expect(salutationElem.getText()).toBe('Hello World!'); 23321 23322 salutationInput.clear(); 23323 salutationInput.sendKeys('Greetings'); 23324 nameInput.clear(); 23325 nameInput.sendKeys('user'); 23326 23327 expect(salutationElem.getText()).toBe('Greetings user!'); 23328 }); 23329 </file> 23330 </example> 23331 */ 23332var ngBindTemplateDirective = ['$interpolate', function($interpolate) { 23333 return function(scope, element, attr) { 23334 // TODO: move this to scenario runner 23335 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); 23336 element.addClass('ng-binding').data('$binding', interpolateFn); 23337 attr.$observe('ngBindTemplate', function(value) { 23338 element.text(value); 23339 }); 23340 }; 23341}]; 23342 23343 23344/** 23345 * @ngdoc directive 23346 * @name ngBindHtml 23347 * 23348 * @description 23349 * Creates a binding that will innerHTML the result of evaluating the `expression` into the current 23350 * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link 23351 * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` 23352 * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in 23353 * core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to 23354 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example 23355 * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. 23356 * 23357 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you 23358 * will have an exception (instead of an exploit.) 23359 * 23360 * @element ANY 23361 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. 23362 * 23363 * @example 23364 23365 <example module="bindHtmlExample" deps="angular-sanitize.js"> 23366 <file name="index.html">
23367 <div ng-controller="ExampleController"> 23368 <p ng-bind-html="myHTML"></p> 23369 </div> 23370 </file> 23371 23372 <file name="script.js"> 23373 angular.module('bindHtmlExample', ['ngSanitize']) 23374 .controller('ExampleController', ['$scope', function($scope) { 23375 $scope.myHTML = 23376 'I am an <code>HTML</code>string with ' + 23377 '<a href="#">links!</a> and other <em>stuff</em>'; 23378 }]); 23379 </file> 23380 23381 <file name="protractor.js" type="protractor"> 23382 it('should check ng-bind-html', function() { 23383 expect(element(by.binding('myHTML')).getText()).toBe( 23384 'I am an HTMLstring with links! and other stuff'); 23385 }); 23386 </file> 23387 </example> 23388 */ 23389var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { 23390 return { 23391 compile: function (tElement) { 23392 tElement.addClass('ng-binding'); 23393 23394 return function (scope, element, attr) { 23395 element.data('$binding', attr.ngBindHtml); 23396 23397 var parsed = $parse(attr.ngBindHtml); 23398 23399 function getStringValue() { 23400 return (parsed(scope) || '').toString(); 23401 } 23402 23403 scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) { 23404 element.html($sce.getTrustedHtml(parsed(scope)) || ''); 23405 }); 23406 }; 23407 } 23408 }; 23409}]; 23410 23411function classDirective(name, selector) { 23412 name = 'ngClass' + name; 23413 return ['$animate', function($animate) { 23414 return { 23415 restrict: 'AC', 23416 link: function(scope, element, attr) { 23417 var oldVal; 23418 23419 scope.$watch(attr[name], ngClassWatchAction, true); 23420 23421 attr.$observe('class', function(value) { 23422 ngClassWatchAction(scope.$eval(attr[name])); 23423 }); 23424 23425 23426 if (name !== 'ngClass') { 23427 scope.$watch('$index', function($index, old$index) { 23428 // jshint bitwise: false 23429 var mod = $index & 1; 23430 if (mod !== (old$index & 1)) { 23431 var classes = arrayClasses(scope.$eval(attr[name])); 23432 mod === selector ? 23433 addClasses(classes) : 23434 removeClasses(classes); 23435 } 23436 }); 23437 } 23438 23439 function addClasses(classes) { 23440 var newClasses = digestClassCounts(classes, 1); 23441 attr.$addClass(newClasses); 23442 } 23443 23444 function removeClasses(classes) { 23445 var newClasses = digestClassCounts(classes, -1); 23446 attr.$removeClass(newClasses); 23447 } 23448 23449 function digestClassCounts (classes, count) { 23450 var classCounts = element.data('$classCounts') || {}; 23451 var classesToUpdate = []; 23452 forEach(classes, function (className) { 23453 if (count > 0 || classCounts[className]) { 23454 classCounts[className] = (classCounts[className] || 0) + count; 23455 if (classCounts[className] === +(count > 0)) { 23456 classesToUpdate.push(className); 23457 } 23458 } 23459 }); 23460 element.data('$classCounts', classCounts); 23461 return classesToUpdate.join(' '); 23462 } 23463 23464 function updateClasses (oldClasses, newClasses) { 23465 var toAdd = arrayDifference(newClasses, oldClasses); 23466 var toRemove = arrayDifference(oldClasses, newClasses); 23467 toRemove = digestClassCounts(toRemove, -1); 23468 toAdd = digestClassCounts(toAdd, 1); 23469 23470 if (toAdd.length === 0) { 23471 $animate.removeClass(element, toRemove); 23472 } else if (toRemove.length === 0) { 23473 $animate.addClass(element, toAdd); 23474 } else { 23475 $animate.setClass(element, toAdd, toRemove); 23476 } 23477 } 23478 23479 function ngClassWatchAction(newVal) { 23480 if (selector === true || scope.$index % 2 === selector) { 23481 var newClasses = arrayClasses(newVal || []); 23482 if (!oldVal) { 23483 addClasses(newClasses); 23484 } else if (!equals(newVal,oldVal)) { 23485 var oldClasses = arrayClasses(oldVal); 23486 updateClasses(oldClasses, newClasses); 23487 } 23488 } 23489 oldVal = shallowCopy(newVal); 23490 } 23491 } 23492 }; 23493 23494 function arrayDifference(tokens1, tokens2) { 23495 var values = []; 23496 23497 outer: 23498 for(var i = 0; i < tokens1.length; i++) { 23499 var token = tokens1[i]; 23500 for(var j = 0; j < tokens2.length; j++) { 23501 if(token == tokens2[j]) continue outer; 23502 } 23503 values.push(token); 23504 } 23505 return values; 23506 } 23507
23508 function arrayClasses (classVal) { 23509 if (isArray(classVal)) { 23510 return classVal; 23511 } else if (isString(classVal)) { 23512 return classVal.split(' '); 23513 } else if (isObject(classVal)) { 23514 var classes = [], i = 0; 23515 forEach(classVal, function(v, k) { 23516 if (v) { 23517 classes = classes.concat(k.split(' ')); 23518 } 23519 }); 23520 return classes; 23521 } 23522 return classVal; 23523 } 23524 }]; 23525} 23526 23527/** 23528 * @ngdoc directive 23529 * @name ngClass 23530 * @restrict AC 23531 * 23532 * @description 23533 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding 23534 * an expression that represents all classes to be added. 23535 * 23536 * The directive operates in three different ways, depending on which of three types the expression 23537 * evaluates to: 23538 * 23539 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class 23540 * names. 23541 * 23542 * 2. If the expression evaluates to an array, each element of the array should be a string that is 23543 * one or more space-delimited class names. 23544 * 23545 * 3. If the expression evaluates to an object, then for each key-value pair of the 23546 * object with a truthy value the corresponding key is used as a class name. 23547 * 23548 * The directive won't add duplicate classes if a particular class was already set. 23549 * 23550 * When the expression changes, the previously added classes are removed and only then the 23551 * new classes are added. 23552 * 23553 * @animations 23554 * add - happens just before the class is applied to the element 23555 * remove - happens just before the class is removed from the element 23556 * 23557 * @element ANY 23558 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result 23559 * of the evaluation can be a string representing space delimited class 23560 * names, an array, or a map of class names to boolean values. In the case of a map, the 23561 * names of the properties whose values are truthy will be added as css classes to the 23562 * element. 23563 * 23564 * @example Example that demonstrates basic bindings via ngClass directive. 23565 <example> 23566 <file name="index.html"> 23567 <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p> 23568 <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br> 23569 <input type="checkbox" ng-model="important"> important (apply "bold" class)<br> 23570 <input type="checkbox" ng-model="error"> error (apply "red" class) 23571 <hr> 23572 <p ng-class="style">Using String Syntax</p> 23573 <input type="text" ng-model="style" placeholder="Type: bold strike red"> 23574 <hr> 23575 <p ng-class="[style1, style2, style3]">Using Array Syntax</p> 23576 <input ng-model="style1" placeholder="Type: bold, strike or red"><br> 23577 <input ng-model="style2" placeholder="Type: bold, strike or red"><br> 23578 <input ng-model="style3" placeholder="Type: bold, strike or red"><br> 23579 </file> 23580 <file name="style.css"> 23581 .strike { 23582 text-decoration: line-through; 23583 } 23584 .bold { 23585 font-weight: bold; 23586 } 23587 .red { 23588 color: red; 23589 } 23590 </file> 23591 <file name="protractor.js" type="protractor"> 23592 var ps = element.all(by.css('p')); 23593 23594 it('should let you toggle the class', function() { 23595 23596 expect(ps.first().getAttribute('class')).not.toMatch(/bold/); 23597 expect(ps.first().getAttribute('class')).not.toMatch(/red/); 23598 23599 element(by.model('important')).click(); 23600 expect(ps.first().getAttribute('class')).toMatch(/bold/); 23601 23602 element(by.model('error')).click(); 23603 expect(ps.first().getAttribute('class')).toMatch(/red/); 23604 }); 23605 23606 it('should let you toggle string example', function() { 23607 expect(ps.get(1).getAttribute('class')).toBe(''); 23608 element(by.model('style')).clear(); 23609 element(by.model('style')).sendKeys('red'); 23610 expect(ps.get(1).getAttribute('class')).toBe('red'); 23611 }); 23612 23613 it('array example should have 3 classes', function() { 23614 expect(ps.last().getAttribute('class')).toBe('');
23615 element(by.model('style1')).sendKeys('bold'); 23616 element(by.model('style2')).sendKeys('strike'); 23617 element(by.model('style3')).sendKeys('red'); 23618 expect(ps.last().getAttribute('class')).toBe('bold strike red'); 23619 }); 23620 </file> 23621 </example> 23622 23623 ## Animations 23624 23625 The example below demonstrates how to perform animations using ngClass. 23626 23627 <example module="ngAnimate" deps="angular-animate.js" animations="true"> 23628 <file name="index.html"> 23629 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'"> 23630 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''"> 23631 <br> 23632 <span class="base-class" ng-class="myVar">Sample Text</span> 23633 </file> 23634 <file name="style.css"> 23635 .base-class { 23636 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 23637 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 23638 } 23639 23640 .base-class.my-class { 23641 color: red; 23642 font-size:3em; 23643 } 23644 </file> 23645 <file name="protractor.js" type="protractor"> 23646 it('should check ng-class', function() { 23647 expect(element(by.css('.base-class')).getAttribute('class')).not. 23648 toMatch(/my-class/); 23649 23650 element(by.id('setbtn')).click(); 23651 23652 expect(element(by.css('.base-class')).getAttribute('class')). 23653 toMatch(/my-class/); 23654 23655 element(by.id('clearbtn')).click(); 23656 23657 expect(element(by.css('.base-class')).getAttribute('class')).not. 23658 toMatch(/my-class/); 23659 }); 23660 </file> 23661 </example> 23662 23663 23664 ## ngClass and pre-existing CSS3 Transitions/Animations 23665 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. 23666 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder 23667 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure 23668 to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and 23669 {@link ngAnimate.$animate#removeclass $animate.removeClass}. 23670 */ 23671var ngClassDirective = classDirective('', true); 23672 23673/** 23674 * @ngdoc directive 23675 * @name ngClassOdd 23676 * @restrict AC 23677 * 23678 * @description 23679 * The `ngClassOdd` and `ngClassEven` directives work exactly as 23680 * {@link ng.directive:ngClass ngClass}, except they work in 23681 * conjunction with `ngRepeat` and take effect only on odd (even) rows. 23682 * 23683 * This directive can be applied only within the scope of an 23684 * {@link ng.directive:ngRepeat ngRepeat}. 23685 * 23686 * @element ANY 23687 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result 23688 * of the evaluation can be a string representing space delimited class names or an array. 23689 * 23690 * @example 23691 <example> 23692 <file name="index.html"> 23693 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> 23694 <li ng-repeat="name in names"> 23695 <span ng-class-odd="'odd'" ng-class-even="'even'"> 23696 {{name}} 23697 </span> 23698 </li> 23699 </ol> 23700 </file> 23701 <file name="style.css"> 23702 .odd { 23703 color: red; 23704 } 23705 .even { 23706 color: blue; 23707 } 23708 </file> 23709 <file name="protractor.js" type="protractor"> 23710 it('should check ng-class-odd and ng-class-even', function() { 23711 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). 23712 toMatch(/odd/); 23713 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). 23714 toMatch(/even/); 23715 }); 23716 </file> 23717 </example> 23718 */ 23719var ngClassOddDirective = classDirective('Odd', 0); 23720 23721/** 23722 * @ngdoc directive 23723 * @name ngClassEven 23724 * @restrict AC 23725 * 23726 * @description 23727 * The `ngClassOdd` and `ngClassEven` directives work exactly as 23728 * {@link ng.directive:ngClass ngClass}, except they work in 23729 * conjunction with `ngRepeat` and take effect only on odd (even) rows. 23730 * 23731 * This directive can be applied only within the scope of an 23732 * {@link ng.directive:ngRepeat ngRepeat}. 23733 * 23734 * @element ANY 23735 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The 23736 * result of the evaluation can be a string representing space delimited class names or an array. 23737 * 23738 * @example 23739 <example> 23740 <file name="index.html"> 23741 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> 23742 <li ng-repeat="name in names">
23743 <span ng-class-odd="'odd'" ng-class-even="'even'"> 23744 {{name}} 23745 </span> 23746 </li> 23747 </ol> 23748 </file> 23749 <file name="style.css"> 23750 .odd { 23751 color: red; 23752 } 23753 .even { 23754 color: blue; 23755 } 23756 </file> 23757 <file name="protractor.js" type="protractor"> 23758 it('should check ng-class-odd and ng-class-even', function() { 23759 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). 23760 toMatch(/odd/); 23761 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). 23762 toMatch(/even/); 23763 }); 23764 </file> 23765 </example> 23766 */ 23767var ngClassEvenDirective = classDirective('Even', 1); 23768 23769/** 23770 * @ngdoc directive 23771 * @name ngCloak 23772 * @restrict AC 23773 * 23774 * @description 23775 * The `ngCloak` directive is used to prevent the Angular html template from being briefly 23776 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this 23777 * directive to avoid the undesirable flicker effect caused by the html template display. 23778 * 23779 * The directive can be applied to the `<body>` element, but the preferred usage is to apply 23780 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering 23781 * of the browser view. 23782 * 23783 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and 23784 * `angular.min.js`. 23785 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). 23786 * 23787 * ```css 23788 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 23789 * display: none !important; 23790 * } 23791 * ``` 23792 * 23793 * When this css rule is loaded by the browser, all html elements (including their children) that 23794 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive 23795 * during the compilation of the template it deletes the `ngCloak` element attribute, making 23796 * the compiled element visible. 23797 * 23798 * For the best result, the `angular.js` script must be loaded in the head section of the html 23799 * document; alternatively, the css rule above must be included in the external stylesheet of the 23800 * application. 23801 * 23802 * Legacy browsers, like IE7, do not provide attribute selector support (a
23802dded in CSS 2.1) so they 23803 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css 23804 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below. 23805 * 23806 * @element ANY 23807 * 23808 * @example 23809 <example> 23810 <file name="index.html"> 23811 <div id="template1" ng-cloak>{{ 'hello' }}</div> 23812 <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div> 23813 </file> 23814 <file name="protractor.js" type="protractor"> 23815 it('should remove the template directive and css class', function() { 23816 expect($('#template1').getAttribute('ng-cloak')). 23817 toBeNull(); 23818 expect($('#template2').getAttribute('ng-cloak')). 23819 toBeNull(); 23820 }); 23821 </file> 23822 </example> 23823 * 23824 */ 23825var ngCloakDirective = ngDirective({ 23826 compile: function(element, attr) { 23827 attr.$set('ngCloak', undefined); 23828 element.removeClass('ng-cloak'); 23829 } 23830}); 23831 23832/** 23833 * @ngdoc directive 23834 * @name ngController 23835 * 23836 * @description 23837 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular 23838 * supports the principles behind the Model-View-Controller design pattern. 23839 * 23840 * MVC components in angular: 23841 * 23842 * * Model â Models are the properties of a scope; scopes are attached to the DOM where scope properties 23843 * are accessed through bindings. 23844 * * View â The template (HTML with data bindings) that is rendered into the View. 23845 * * Controller â The `ngController` directive specifies a Controller class; the class contains business 23846 * logic behind the application to decorate the scope with functions and values 23847 * 23848 * Note that you can also attach controllers to the DOM by declaring it in a route definition 23849 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller 23850 * again using `ng-controller` in the template itself. This will cause the controller to be attached 23851 * and executed twice. 23852 * 23853 * @element ANY 23854 * @scope 23855 * @param {expression} ngController Name of a globally accessible constructor function or an 23856 * {@link guide/expression expression} that on the current scope evaluates to a 23857 * constructor function. The controller instance can be published into a scope property 23858 * by specifying `as propertyName`. 23859 * 23860 * @example 23861 * Here is a simple form for editing user contact information. Adding, removing, clearing, and 23862 * greeting are methods declared on the controller (see source tab). These methods can 23863 * easily be called from the angular markup. Any changes to the data are automatically reflected 23864 * in the View without the need for a manual update. 23865 * 23866 * Two different declaration styles are included below: 23867 * 23868 * * one binds methods and properties directly onto the controller using `this`: 23869 * `ng-controller="SettingsController1 as settings"` 23870 * * one injects `$scope` into the controller: 23871 * `ng-controller="SettingsController2"` 23872 * 23873 * The second option is more common in the Angular community, and is generally used in boilerplates 23874 * and in this guide. However, there are advantages to binding properties directly to the controller 23875 * and avoiding scope. 23876 * 23877 * * Using `controller as` makes it obvious which controller you are accessing in the template when 23878 * multiple controllers apply to an element. 23879 * * If you are writing your controllers as classes you have easier access to the properties and 23880 * methods, which will appear on the scope, from inside the controller code. 23881 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal 23882 * inheritance masking primitives. 23883 * 23884 * This example demonstrates the `controller as` syntax. 23885 * 23886 * <example name="ngControllerAs" module="controllerAsExample"> 23887 * <file name="index.html"> 23888 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings"> 23889 * Name: <input type="text" ng-model="settings.name"/> 23890 * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/> 23891 * Contact: 23892 * <ul> 23893 * <li ng-repeat="contact in settings.contacts"> 23894 * <select ng-model="contact.type"> 23895 * <option>phone</option> 23896 * <option>email</option> 23897 * </select> 23898 * <input type="text" ng-model="contact.value"/> 23899 * [ <a href="" ng-click="settings.clearContact(contact)">clear</a> 23900 * | <a href="" ng-click="settings.removeContact(contact)">X</a> ] 23901 * </li> 23902 * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li> 23903 * </ul> 23904 * </div> 23905 * </file> 23906 * <file name="app.js">
23907 * angular.module('controllerAsExample', []) 23908 * .controller('SettingsController1', SettingsController1); 23909 * 23910 * function SettingsController1() { 23911 * this.name = "John Smith"; 23912 * this.contacts = [ 23913 * {type: 'phone', value: '408 555 1212'}, 23914 * {type: 'email', value: '[email protected]'} ]; 23915 * } 23916 * 23917 * SettingsController1.prototype.greet = function() { 23918 * alert(this.name); 23919 * }; 23920 * 23921 * SettingsController1.prototype.addContact = function() { 23922 * this.contacts.push({type: 'email', value: '[email protected]'}); 23923 * }; 23924 * 23925 * SettingsController1.prototype.removeContact = function(contactToRemove) { 23926 * var index = this.contacts.indexOf(contactToRemove); 23927 * this.contacts.splice(index, 1); 23928 * }; 23929 * 23930 * SettingsController1.prototype.clearContact = function(contact) { 23931 * contact.type = 'phone'; 23932 * contact.value = ''; 23933 * }; 23934 * </file> 23935 * <file name="protractor.js" type="protractor"> 23936 * it('should check controller as', function() { 23937 * var container = element(by.id('ctrl-as-exmpl')); 23938 * expect(container.element(by.model('settings.name')) 23939 * .getAttribute('value')).toBe('John Smith'); 23940 * 23941 * var firstRepeat = 23942 * container.element(by.repeater('contact in settings.contacts').row(0)); 23943 * var secondRepeat = 23944 * container.element(by.repeater('contact in settings.contacts').row(1)); 23945 * 23946 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 23947 * .toBe('408 555 1212'); 23948 * 23949 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) 23950 * .toBe('[email protected]'); 23951 * 23952 * firstRepeat.element(by.linkText('clear')).click(); 23953 * 23954 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 23955 * .toBe(''); 23956 * 23957 * container.element(by.linkText('add')).click(); 23958 * 23959 * expect(container.element(by.repeater('contact in settings.contacts').row(2)) 23960 * .element(by.model('contact.value')) 23961 * .getAttribute('value')) 23962 * .toBe('[email protected]'); 23963 * }); 23964 * </file> 23965 * </example> 23966 * 23967 * This example demonstrates the "attach to `$scope`" style of controller. 23968 * 23969 * <example name="ngController" module="controllerExample"> 23970 * <file name="index.html"> 23971 * <div id="ctrl-exmpl" ng-controller="SettingsController2"> 23972 * Name: <input type="text" ng-model="name"/> 23973 * [ <a href="" ng-click="greet()">greet</a> ]<br/> 23974 * Contact: 23975 * <ul> 23976 * <li ng-repeat="contact in contacts"> 23977 * <select ng-model="contact.type"> 23978 * <option>phone</option> 23979 * <option>email</option> 23980 * </select> 23981 * <input type="text" ng-model="contact.value"/> 23982 * [ <a href="" ng-click="clearContact(contact)">clear</a> 23983 * | <a href="" ng-click="removeContact(contact)">X</a> ] 23984 * </li> 23985 * <li>[ <a href="" ng-click="addContact()">add</a> ]</li> 23986 * </ul> 23987 * </div> 23988 * </file> 23989 * <file name="app.js"> 23990 * angular.module('controllerExample', []) 23991 * .controller('SettingsController2', ['$scope', SettingsController2]); 23992 * 23993 * function SettingsController2($scope) { 23994 * $scope.name = "John Smith"; 23995 * $scope.contacts = [ 23996 * {type:'phone', value:'408 555 1212'}, 23997 * {type:'email', value:'[email protected]'} ]; 23998 * 23999 * $scope.greet = function() { 24000 * alert($scope.name); 24001 * }; 24002 * 24003 * $scope.addContact = function() { 24004 * $scope.contacts.push({type:'email', value:'[email protected]'}); 24005 * }; 24006 * 24007 * $scope.removeContact = function(contactToRemove) { 24008 * var index = $scope.contacts.indexOf(contactToRemove); 24009 * $scope.contacts.splice(index, 1); 24010 * }; 24011 * 24012 * $scope.clearContact = function(contact) { 24013 * contact.type = 'phone'; 24014 * contact.value = ''; 24015 * }; 24016 * } 24017 * </file> 24018 * <file name="protractor.js" type="protractor"> 24019 * it('should check controller', function() { 24020 * var container = element(by.id('ctrl-exmpl')); 24021 * 24022 * expect(container.element(by.model('name')) 24023 * .getAttribute('value')).toBe('John Smith'); 24024 * 24025 * var firstRepeat = 24026 * container.element(by.repeater('contact in contacts').row(0)); 24027 * var secondRepeat = 24028 * container.element(by.repeater('contact in contacts').row(1)); 24029 * 24030 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 24031 * .toBe('408 555 1212'); 24032 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) 24033 * .toBe('[email protected]'); 24034 * 24035 * firstRepeat.element(by.linkText('clear')).click(); 24036 * 24037 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) 24038 * .toBe(''); 24039 * 24040 * container.element(by.linkText('add')).click(); 24041 * 24042 * expect(container.element(by.repeater('contact in contacts').row(2)) 24043 * .element(by.model('contact.value')) 24044 * .getAttribute('value')) 24045 * .toBe('[email protected]'); 24046 * });
24047 * </file> 24048 *</example> 24049 24050 */ 24051var ngControllerDirective = [function() { 24052 return { 24053 scope: true, 24054 controller: '@', 24055 priority: 500 24056 }; 24057}]; 24058 24059/** 24060 * @ngdoc directive 24061 * @name ngCsp 24062 * 24063 * @element html 24064 * @description 24065 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. 24066 * 24067 * This is necessary when developing things like Google Chrome Extensions. 24068 * 24069 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). 24070 * For Angular to be CSP compatible there are only two things that we need to do differently: 24071 * 24072 * - don't use `Function` constructor to generate optimized value getters 24073 * - don't inject custom stylesheet into the document 24074 * 24075 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` 24076 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will 24077 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will 24078 * be raised. 24079 * 24080 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically 24081 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). 24082 * To make those directives work in CSP mode, include the `angular-csp.css` manually. 24083 * 24084 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This 24085 * autodetection however triggers a CSP error to be logged in the console: 24086 * 24087 * ``` 24088 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of 24089 * script in the following Content Security Policy directive: "default-src 'self'". Note that 24090 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. 24091 * ``` 24092 * 24093 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` 24094 * directive on the root element of the application or on the `angular.js` script tag, whichever 24095 * appears first in the html document. 24096 * 24097 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* 24098 * 24099 * @example 24100 * This example shows how to apply the `ngCsp` directive to the `html` tag. 24101 ```html 24102 <!doctype html> 24103 <html ng-app ng-csp> 24104 ... 24105 ... 24106 </html> 24107 ``` 24108 */ 24109 24110// ngCsp is not implemented as a proper directive any more, because we need it be processed while we 24111// bootstrap the system (before $parse is instantiated), for this reason we just have 24112// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc 24113 24114/** 24115 * @ngdoc directive 24116 * @name ngClick 24117 * 24118 * @description 24119 * The ngClick directive allows you to specify custom behavior when 24120 * an element is clicked. 24121 * 24122 * @element ANY 24123 * @priority 0 24124 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon 24125 * click. ({@link guide/expression#-event- Event object is available as `$event`}) 24126 * 24127 * @example 24128 <example> 24129 <file name="index.html"> 24130 <button ng-click="count = count + 1" ng-init="count=0"> 24131 Increment 24132 </button>
24133 <span> 24134 count: {{count}} 24135 <span> 24136 </file> 24137 <file name="protractor.js" type="protractor"> 24138 it('should check ng-click', function() { 24139 expect(element(by.binding('count')).getText()).toMatch('0'); 24140 element(by.css('button')).click(); 24141 expect(element(by.binding('count')).getText()).toMatch('1'); 24142 }); 24143 </file> 24144 </example> 24145 */ 24146/* 24147 * A directive that allows creation of custom onclick handlers that are defined as angular 24148 * expressions and are compiled and executed within the current scope. 24149 * 24150 * Events that are handled via these handler are always configured not to propagate further. 24151 */ 24152var ngEventDirectives = {}; 24153 24154// For events that might fire synchronously during DOM manipulation 24155// we need to execute their event handlers asynchronously using $evalAsync, 24156// so that they are not executed in an inconsistent state. 24157var forceAsyncEvents = { 24158 'blur': true, 24159 'focus': true 24160}; 24161forEach( 24162 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), 24163 function(eventName) { 24164 var directiveName = directiveNormalize('ng-' + eventName); 24165 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { 24166 return { 24167 compile: function($element, attr) { 24168 var fn = $parse(attr[directiveName]); 24169 return function ngEventHandler(scope, element) { 24170 element.on(eventName, function(event) { 24171 var callback = function() { 24172 fn(scope, {$event:event}); 24173 }; 24174 if (forceAsyncEvents[eventName] && $rootScope.$$phase) { 24175 scope.$evalAsync(callback); 24176 } else { 24177 scope.$apply(callback); 24178 } 24179 }); 24180 }; 24181 } 24182 }; 24183 }]; 24184 } 24185); 24186 24187/** 24188 * @ngdoc directive 24189 * @name ngDblclick 24190 * 24191 * @description 24192 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. 24193 * 24194 * @element ANY 24195 * @priority 0 24196 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon 24197 * a dblclick. (The Event object is available as `$event`) 24198 * 24199 * @example 24200 <example> 24201 <file name="index.html"> 24202 <button ng-dblclick="count = count + 1" ng-init="count=0"> 24203 Increment (on double click) 24204 </button> 24205 count: {{count}} 24206 </file> 24207 </example> 24208 */ 24209 24210 24211/** 24212 * @ngdoc directive 24213 * @name ngMousedown 24214 * 24215 * @description 24216 * The ngMousedown directive allows you to specify custom behavior on mousedown event. 24217 * 24218 * @element ANY 24219 * @priority 0 24220 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon 24221 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) 24222 * 24223 * @example 24224 <example> 24225 <file name="index.html"> 24226 <button ng-mousedown="count = count + 1" ng-init="count=0"> 24227 Increment (on mouse down) 24228 </button> 24229 count: {{count}} 24230 </file> 24231 </example> 24232 */ 24233 24234 24235/** 24236 * @ngdoc directive 24237 * @name ngMouseup 24238 * 24239 * @description 24240 * Specify custom behavior on mouseup event. 24241 * 24242 * @element ANY 24243 * @priority 0 24244 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon 24245 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) 24246 * 24247 * @example 24248 <example> 24249 <file name="index.html"> 24250 <button ng-mouseup="count = count + 1" ng-init="count=0"> 24251 Increment (on mouse up) 24252 </button> 24253 count: {{count}} 24254 </file> 24255 </example> 24256 */ 24257 24258/** 24259 * @ngdoc directive 24260 * @name ngMouseover 24261 * 24262 * @description 24263 * Specify custom behavior on mouseover event. 24264 * 24265 * @element ANY 24266 * @priority 0 24267 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon 24268 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) 24269 * 24270 * @example 24271 <example> 24272 <file name="index.html"> 24273 <button ng-mouseover="count = count + 1" ng-init="count=0"> 24274 Increment (when mouse is over) 24275 </button> 24276 count: {{count}} 24277 </file> 24278 </example> 24279 */ 24280 24281 24282/** 24283 * @ngdoc directive 24284 * @name ngMouseenter 24285 * 24286 * @description 24287 * Specify custom behavior on mouseenter event. 24288 * 24289 * @element ANY 24290 * @priority 0 24291 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon 24292 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) 24293 * 24294 * @example 24295 <example> 24296 <file name="index.html"> 24297 <button ng-mouseenter="count = count + 1" ng-init="count=0"> 24298 Increment (when mouse enters) 24299 </button> 24300 count: {{count}} 24301 </file> 24302 </example> 24303 */ 24304 24305 24306/** 24307 * @ngdoc directive 24308 * @name ngMouseleave 24309 * 24310 * @description 24311 * Specify custom behavior on mouseleave event. 24312 * 24313 * @element ANY 24314 * @priority 0 24315 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon 24316 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) 24317 * 24318 * @example 24319 <example> 24320 <file name="index.html"> 24321 <button ng-mouseleave="count = count + 1" ng-init="count=0"> 24322 Increment (when mouse leaves) 24323 </button> 24324 count: {{count}} 24325 </file> 24326 </example> 24327 */ 24328 24329 24330/** 24331 * @ngdoc directive 24332 * @name ngMousemove 24333 * 24334 * @description 24335 * Specify custom behavior on mousemove event. 24336 * 24337 * @element ANY 24338 * @priority 0 24339 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon 24340 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) 24341 * 24342 * @example 24343 <example> 24344 <file name="index.html"> 24345 <button ng-mousemove="count = count + 1" ng-init="count=0"> 24346 Increment (when mouse moves) 24347 </button> 24348 count: {{count}} 24349 </file> 24350 </example> 24351 */ 24352 24353 24354/** 24355 * @ngdoc directive 24356 * @name ngKeydown 24357 * 24358 * @description 24359 * Specify custom behavior on keydown event. 24360 * 24361 * @element ANY 24362 * @priority 0 24363 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon 24364 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) 24365 * 24366 * @example 24367 <example> 24368 <file name="index.html"> 24369 <input ng-keydown="count = count + 1" ng-init="count=0"> 24370 key down count: {{count}} 24371 </file> 24372 </example> 24373 */ 24374 24375 24376/** 24377 * @ngdoc directive 24378 * @name ngKeyup 24379 * 24380 * @description 24381 * Specify custom behavior on keyup event. 24382 * 24383 * @element ANY 24384 * @priority 0 24385 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon 24386 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) 24387 * 24388 * @example 24389 <example> 24390 <file name="index.html"> 24391 <p>Typing in the input box below updates the key count</p> 24392 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}} 24393 24394 <p>Typing in the input box below updates the keycode</p> 24395 <input ng-keyup="event=$event"> 24396 <p>event keyCode: {{ event.keyCode }}</p> 24397 <p>event altKey: {{ event.altKey }}</p> 24398 </file> 24399 </example> 24400 */ 24401 24402 24403/** 24404 * @ngdoc directive 24405 * @name ngKeypress 24406 * 24407 * @description 24408 * Specify custom behavior on keypress event. 24409 * 24410 * @element ANY 24411 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon 24412 * keypress. ({@link guide/expression#-event- Event object is available as `$event`} 24413 * and can be interrogated for keyCode, altKey, etc.) 24414 * 24415 * @example 24416 <example> 24417 <file name="index.html"> 24418 <input ng-keypress="count = count + 1" ng-init="count=0"> 24419 key press count: {{count}} 24420 </file> 24421 </example> 24422 */ 24423 24424 24425/** 24426 * @ngdoc directive 24427 * @name ngSubmit 24428 * 24429 * @description 24430 * Enables binding angular expressions to onsubmit events. 24431 * 24432 * Additionally it prevents the default action (which for form means
24432sending the request to the 24433 * server and reloading the current page), but only if the form does not contain `action`, 24434 * `data-action`, or `x-action` attributes. 24435 * 24436 * <div class="alert alert-warning"> 24437 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and 24438 * `ngSubmit` handlers together. See the 24439 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} 24440 * for a detailed discussion of when `ngSubmit` may be triggered. 24441 * </div> 24442 * 24443 * @element form 24444 * @priority 0 24445 * @param {expression} ngSubmit {@link guide/expression Expression} to eval. 24446 * ({@link guide/expression#-event- Event object is available as `$event`}) 24447 * 24448 * @example 24449 <example module="submitExample"> 24450 <file name="index.html"> 24451 <script> 24452 angular.module('submitExample', []) 24453 .controller('ExampleController', ['$scope', function($scope) { 24454 $scope.list = []; 24455 $scope.text = 'hello'; 24456 $scope.submit = function() { 24457 if ($scope.text) { 24458 $scope.list.push(this.text); 24459 $scope.text = ''; 24460 } 24461 }; 24462 }]); 24463 </script>
24464 <form ng-submit="submit()" ng-controller="ExampleController"> 24465 Enter text and hit enter: 24466 <input type="text" ng-model="text" name="text" /> 24467 <input type="submit" id="submit" value="Submit" /> 24468 <pre>list={{list}}</pre> 24469 </form> 24470 </file> 24471 <file name="protractor.js" type="protractor"> 24472 it('should check ng-submit', function() { 24473 expect(element(by.binding('list')).getText()).toBe('list=[]'); 24474 element(by.css('#submit')).click(); 24475 expect(element(by.binding('list')).getText()).toContain('hello'); 24476 expect(element(by.model('text')).getAttribute('value')).toBe(''); 24477 }); 24478 it('should ignore empty strings', function() { 24479 expect(element(by.binding('list')).getText()).toBe('list=[]'); 24480 element(by.css('#submit')).click(); 24481 element(by.css('#submit')).click(); 24482 expect(element(by.binding('list')).getText()).toContain('hello'); 24483 }); 24484 </file> 24485 </example> 24486 */ 24487 24488/** 24489 * @ngdoc directive 24490 * @name ngFocus 24491 * 24492 * @description 24493 * Specify custom behavior on focus event. 24494 * 24495 * Note: As the `focus` event is executed synchronously when calling `input.focus()` 24496 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired 24497 * during an `$apply` to ensure a consistent state. 24498 * 24499 * @element window, input, select, textarea, a 24500 * @priority 0 24501 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon 24502 * focus. ({@link guide/expression#-event- Event object is available as `$event`}) 24503 * 24504 * @example 24505 * See {@link ng.directive:ngClick ngClick} 24506 */ 24507 24508/** 24509 * @ngdoc directive 24510 * @name ngBlur 24511 * 24512 * @description 24513 * Specify custom behavior on blur event. 24514 * 24515 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when 24516 * an element has lost focus. 24517 * 24518 * Note: As the `blur` event is executed synchronously also during DOM manipulations 24519 * (e.g. removing a focussed input), 24520 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired 24521 * during an `$apply` to ensure a consistent state. 24522 * 24523 * @element window, input, select, textarea, a 24524 * @priority 0 24525 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon 24526 * blur. ({@link guide/expression#-event- Event object is available as `$event`}) 24527 * 24528 * @example 24529 * See {@link ng.directive:ngClick ngClick} 24530 */ 24531 24532/** 24533 * @ngdoc directive 24534 * @name ngCopy 24535 * 24536 * @description 24537 * Specify custom behavior on copy event. 24538 * 24539 * @element window, input, select, textarea, a 24540 * @priority 0 24541 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon 24542 * copy. ({@link guide/expression#-event- Event object is available as `$event`}) 24543 * 24544 * @example 24545 <example> 24546 <file name="index.html"> 24547 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value"> 24548 copied: {{copied}} 24549 </file> 24550 </example> 24551 */ 24552 24553/** 24554 * @ngdoc directive 24555 * @name ngCut 24556 * 24557 * @description 24558 * Specify custom behavior on cut event. 24559 * 24560 * @element window, input, select, textarea, a 24561 * @priority 0 24562 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon 24563 * cut. ({@link guide/expression#-event- Event object is available as `$event`}) 24564 * 24565 * @example 24566 <example> 24567 <file name="index.html"> 24568 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value"> 24569 cut: {{cut}} 24570 </file> 24571 </example> 24572 */ 24573 24574/** 24575 * @ngdoc directive 24576 * @name ngPaste 24577 * 24578 * @description 24579 * Specify custom behavior on paste event. 24580 * 24581 * @element window, input, select, textarea, a 24582 * @priority 0 24583 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon 24584 * paste. ({@link guide/expression#-event- Event object is available as `$event`}) 24585 * 24586 * @example 24587 <example> 24588 <file name="index.html"> 24589 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'> 24590 pasted: {{paste}} 24591 </file> 24592 </example> 24593 */ 24594 24595/** 24596 * @ngdoc directive 24597 * @name ngIf 24598 * @restrict A 24599 * 24600 * @description 24601 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an 24602 * {expression}. If the expression assigned to `ngIf` evaluates to a false 24603 * value then the element is removed from the DOM, otherwise a clone of the 24604 * element is reinserted into the DOM. 24605 * 24606 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the 24607 * element in the DOM rather than changing its visibility via the `display` css property. A common 24608 * case when this difference is significant is when using css selectors that rely on an element's 24609 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
24609s. 24610 * 24611 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope 24612 * is created when the element is restored. The scope created within `ngIf` inherits from 24613 * its parent scope using 24614 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance). 24615 * An important implication of this is if `ngModel` is used within `ngIf` to bind to 24616 * a javascript primitive defined in the parent scope. In this case any modifications made to the 24617 * variable within the child scope will override (hide) the value in the parent scope. 24618 * 24619 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior 24620 * is if an element's class attribute is directly modified after it's compiled, using something like 24621 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element 24622 * the added class will be lost because the original compiled state is used to regenerate the element. 24623 * 24624 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` 24625 * and `leave` effects. 24626 * 24627 * @animations 24628 * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container 24629 * leave - happens just before the ngIf contents are removed from the DOM 24630 * 24631 * @element ANY 24632 * @scope 24633 * @priority 600 24634 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then 24635 * the element is removed from the DOM tree. If it is truthy a copy of the compiled 24636 * element is added to the DOM tree. 24637 * 24638 * @example 24639 <example module="ngAnimate" deps="angular-animate.js" animations="true"> 24640 <file name="index.html"> 24641 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> 24642 Show when checked: 24643 <span ng-if="checked" class="animate-if"> 24644 I'm removed when the checkbox is unchecked. 24645 </span> 24646 </file> 24647 <file name="animations.css"> 24648 .animate-if { 24649 background:white; 24650 border:1px solid black; 24651 padding:10px; 24652 } 24653 24654 .animate-if.ng-enter, .animate-if.ng-leave { 24655 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 24656 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 24657 } 24658 24659 .animate-if.ng-enter, 24660 .animate-if.ng-leave.ng-leave-active { 24661 opacity:0; 24662 } 24663 24664 .animate-if.ng-leave, 24665 .animate-if.ng-enter.ng-enter-active { 24666 opacity:1; 24667 } 24668 </file> 24669 </example> 24670 */ 24671var ngIfDirective = ['$animate', function($animate) { 24672 return { 24673 transclude: 'element', 24674 priority: 600, 24675 terminal: true, 24676 restrict: 'A', 24677 $$tlb: true, 24678 link: function ($scope, $element, $attr, ctrl, $transclude) { 24679 var block, childScope, previousElements; 24680 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { 24681 24682 if (toBoolean(value)) { 24683 if (!childScope) { 24684 childScope = $scope.$new(); 24685 $transclude(childScope, function (clone) { 24686 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); 24687 // Note: We only need the first/last node of the cloned nodes. 24688 // However, we need to keep the reference to the jqlite wrapper as it might be changed later 24689 // by a directive with templateUrl when its template arrives. 24690 block = { 24691 clone: clone 24692 }; 24693 $animate.enter(clone, $element.parent(), $element); 24694 }); 24695 } 24696 } else { 24697 if(previousElements) { 24698 previousElements.remove(); 24699 previousElements = null; 24700 } 24701 if(childScope) { 24702 childScope.$destroy(); 24703 childScope = null; 24704 } 24705 if(block) { 24706 previousElements = getBlockElements(block.clone); 24707 $animate.leave(previousElements, function() { 24708 previousElements = null; 24709 }); 24710 block = null; 24711 } 24712 } 24713 }); 24714 } 24715 }; 24716}]; 24717 24718/** 24719 * @ngdoc directive 24720 * @name ngInclude 24721 * @restrict ECA 24722 * 24723 * @description 24724 * Fetches, compiles and includes an external HTML fragment. 24725 * 24726 * By default, the template URL is restricted to the same domain and protocol as the 24727 * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl 24728 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols 24729 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or 24730 * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link 24731 * ng.$sce Strict Contextual Escaping}. 24732 * 24733 * In addition, the browser's 24734 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) 24735 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) 24736 * policy may further restrict whether the template is successfully loaded. 24737 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` 24738 * access on some browsers. 24739 * 24740 * @animations 24741 * enter - animation is used to bring new content into the browser. 24742 * leave - animation is used to animate existing content away. 24743 * 24744 * The enter and leave animation occur concurrently. 24745 * 24746 * @scope 24747 * @priority 400 24748 * 24749 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, 24750 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. 24751 * @param {string=} onload Expression to evaluate when a new partial is loaded. 24752 * 24753 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll 24754 * $anchorScroll} to scroll the viewport after the content is loaded. 24755 * 24756 * - If the attribute is not set, disable scrolling. 24757 * - If the attribute is set without value, enable scrolling. 24758 * - Otherwise enable scrolling only if the expression evaluates to truthy value. 24759 * 24760 * @example 24761 <example module="includeExample" deps="angular-animate.js" animations="true"> 24762 <file name="index.html">
24763 <div ng-controller="ExampleController"> 24764 <select ng-model="template" ng-options="t.name for t in templates"> 24765 <option value="">(blank)</option> 24766 </select> 24767 url of the template: <tt>{{template.url}}</tt> 24768 <hr/> 24769 <div class="slide-animate-container"> 24770 <div class="slide-animate" ng-include="template.url"></div> 24771 </div> 24772 </div> 24773 </file> 24774 <file name="script.js"> 24775 angular.module('includeExample', ['ngAnimate']) 24776 .controller('ExampleController', ['$scope', function($scope) { 24777 $scope.templates = 24778 [ { name: 'template1.html', url: 'template1.html'}, 24779 { name: 'template2.html', url: 'template2.html'} ]; 24780 $scope.template = $scope.templates[0]; 24781 }]); 24782 </file> 24783 <file name="template1.html"> 24784 Content of template1.html 24785 </file> 24786 <file name="template2.html"> 24787 Content of template2.html 24788 </file> 24789 <file name="animations.css"> 24790 .slide-animate-container { 24791 position:relative; 24792 background:white; 24793 border:1px solid black; 24794 height:40px; 24795 overflow:hidden; 24796 } 24797 24798 .slide-animate { 24799 padding:10px; 24800 } 24801 24802 .slide-animate.ng-enter, .slide-animate.ng-leave { 24803 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 24804 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 24805 24806 position:absolute; 24807 top:0; 24808 left:0; 24809 right:0; 24810 bottom:0; 24811 display:block; 24812 padding:10px; 24813 } 24814 24815 .slide-animate.ng-enter { 24816 top:-50px; 24817 } 24818 .slide-animate.ng-enter.ng-enter-active { 24819 top:0; 24820 } 24821 24822 .slide-animate.ng-leave { 24823 top:0; 24824 } 24825 .slide-animate.ng-leave.ng-leave-active { 24826 top:50px; 24827 } 24828 </file> 24829 <file name="protractor.js" type="protractor"> 24830 var templateSelect = element(by.model('template')); 24831 var includeElem = element(by.css('[ng-include]')); 24832 24833 it('should load template1.html', function() { 24834 expect(includeElem.getText()).toMatch(/Content of template1.html/); 24835 }); 24836 24837 it('should load template2.html', function() { 24838 if (browser.params.browser == 'firefox') { 24839 // Firefox can't handle using selects 24840 // See https://github.com/angular/protractor/issues/480 24841 return; 24842 } 24843 templateSelect.click(); 24844 templateSelect.all(by.css('option')).get(2).click(); 24845 expect(includeElem.getText()).toMatch(/Content of template2.html/); 24846 }); 24847 24848 it('should change to blank', function() { 24849 if (browser.params.browser == 'firefox') { 24850 // Firefox can't handle using selects 24851 return; 24852 } 24853 templateSelect.click(); 24854 templateSelect.all(by.css('option')).get(0).click(); 24855 expect(includeElem.isPresent()).toBe(false); 24856 }); 24857 </file> 24858 </example> 24859 */ 24860 24861 24862/** 24863 * @ngdoc event 24864 * @name ngInclude#$includeContentRequested 24865 * @eventType emit on the scope ngInclude was declared in 24866 * @description 24867 * Emitted every time the ngInclude content is requested. 24868 */ 24869 24870 24871/** 24872 * @ngdoc event 24873 * @name ngInclude#$includeContentLoaded 24874 * @eventType emit on the current ngInclude scope 24875 * @description 24876 * Emitted every time the ngInclude content is reloaded. 24877 */ 24878var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce', 24879 function($http, $templateCache, $anchorScroll, $animate, $sce) { 24880 return { 24881 restrict: 'ECA', 24882 priority: 400, 24883 terminal: true, 24884 transclude: 'element', 24885 controller: angular.noop, 24886 compile: function(element, attr) { 24887 var srcExp = attr.ngInclude || attr.src, 24888 onloadExp = attr.onload || '', 24889 autoScrollExp = attr.autoscroll; 24890 24891 return function(scope, $element, $attr, ctrl, $transclude) { 24892 var changeCounter = 0, 24893 currentScope, 24894 previousElement, 24895 currentElement; 24896 24897 var cleanupLastIncludeContent = function() { 24898 if(previousElement) { 24899 previousElement.remove(); 24900 previousElement = null; 24901 } 24902 if(currentScope) { 24903 currentScope.$destroy(); 24904 currentScope = null; 24905 } 24906 if(currentElement) { 24907 $animate.leave(currentElement, function() { 24908 previousElement = null; 24909 }); 24910 previousElement = currentElement; 24911 currentElement = null; 24912 } 24913 }; 24914 24915 scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { 24916 var afterAnimation = function() { 24917 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { 24918 $anchorScroll(); 24919 } 24920 }; 24921 var thisChangeId = ++changeCounter; 24922 24923 if (src) { 24924 $http.get(src, {cache: $templateCache}).success(function(response) { 24925 if (thisChangeId !== changeCounter) return; 24926 var newScope = scope.$new(); 24927 ctrl.template = response; 24928 24929 // Note: This will also link all children of ng-include that were contained in the original 24930 // html. If that content contains controllers, ... they could pollute/change the scope. 24931 // However, using ng-include on an element with additional content does not make sense... 24932 // Note: We can't remove them in the cloneAttchFn of $transclude as that 24933 // function is called before linking the content, which would apply child 24934 // directives to non existing elements. 24935 var clone = $transclude(newScope, function(clone) { 24936 cleanupLastIncludeContent(); 24937 $animate.enter(clone, null, $element, afterAnimation); 24938 }); 24939 24940 currentScope = newScope; 24941 currentElement = clone; 24942 24943 currentScope.$emit('$includeContentLoaded'); 24944 scope.$eval(onloadExp); 24945 }).error(function() { 24946 if (thisChangeId === changeCounter) cleanupLastIncludeContent(); 24947 }); 24948 scope.$emit('$includeContentRequested'); 24949 } else { 24950 cleanupLastIncludeContent(); 24951 ctrl.template = null; 24952 } 24953 }); 24954 }; 24955 } 24956 }; 24957}]; 24958 24959// This directive is called during the $transclude call of the first `ngInclude` directive. 24960// It will replace and compile the content of the element with the loaded template. 24961// We need this directive so that the element content is already filled when 24962// the link function of another directive on the same element as ngInclude 24963// is called. 24964var ngIncludeFillContentDirective = ['$compile', 24965 function($compile) { 24966 return { 24967 restrict: 'ECA', 24968 priority: -400, 24969 require: 'ngInclude', 24970 link: function(scope, $element, $attr, ctrl) { 24971 $element.html(ctrl.template); 24972 $compile($element.contents())(scope); 24973 } 24974 }; 24975 }]; 24976 24977/** 24978 * @ngdoc directive 24979 * @name ngInit 24980 * @restrict AC 24981 * 24982 * @description 24983 * The `ngInit` directive allows you to evaluate an expression in the 24984 * current scope. 24985 * 24986 * <div class="alert alert-error"> 24987 * The only appropriate use of `ngInit` is for aliasing special properties of 24988 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you 24989 * should use {@link guide/controller controllers} rather than `ngInit` 24990 * to initialize values on a scope. 24991 * </div> 24992 * <div class="alert alert-warning"> 24993 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
24994 * sure you have parenthesis for correct precedence: 24995 * <pre class="prettyprint"> 24996 * <div ng-init="test1 = (data | orderBy:'name')"></div> 24997 * </pre> 24998 * </div> 24999 * 25000 * @priority 450 25001 * 25002 * @element ANY 25003 * @param {expression} ngInit {@link guide/expression Expression} to eval. 25004 * 25005 * @example 25006 <example module="initExample"> 25007 <file name="index.html"> 25008 <script> 25009 angular.module('initExample', []) 25010 .controller('ExampleController', ['$scope', function($scope) { 25011 $scope.list = [['a', 'b'], ['c', 'd']]; 25012 }]); 25013 </script> 25014 <div ng-controller="ExampleController"> 25015 <div ng-repeat="innerList in list" ng-init="outerIndex = $index"> 25016 <div ng-repeat="value in innerList" ng-init="innerIndex = $index"> 25017 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span> 25018 </div> 25019 </div> 25020 </div> 25021 </file> 25022 <file name="protractor.js" type="protractor"> 25023 it('should alias index positions', function() { 25024 var elements = element.all(by.css('.example-init')); 25025 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); 25026 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); 25027 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); 25028 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); 25029 }); 25030 </file> 25031 </example> 25032 */ 25033var ngInitDirective = ngDirective({ 25034 priority: 450, 25035 compile: function() { 25036 return { 25037 pre: function(scope, element, attrs) { 25038 scope.$eval(attrs.ngInit); 25039 } 25040 }; 25041 } 25042}); 25043 25044/** 25045 * @ngdoc directive 25046 * @name ngNonBindable 25047 * @restrict AC 25048 * @priority 1000 25049 * 25050 * @description 25051 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current 25052 * DOM element. This is useful if the element contains what appears to be Angular directives and 25053 * bindings but which should be ignored by Angular. This could be the case if you have a site that 25054 * displays snippets of code, for instance. 25055 * 25056 * @element ANY 25057 * 25058 * @example 25059 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, 25060 * but the one wrapped in `ngNonBindable` is left alone. 25061 * 25062 * @example 25063 <example> 25064 <file name="index.html"> 25065 <div>Normal: {{1 + 2}}</div> 25066 <div ng-non-bindable>Ignored: {{1 + 2}}</div> 25067 </file> 25068 <file name="protractor.js" type="protractor"> 25069 it('should check ng-non-bindable', function() { 25070 expect(element(by.binding('1 + 2')).getText()).toContain('3'); 25071 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); 25072 }); 25073 </file> 25074 </example> 25075 */ 25076var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); 25077 25078/** 25079 * @ngdoc directive 25080 * @name ngPluralize 25081 * @restrict EA 25082 * 25083 * @description 25084 * `ngPluralize` is a directive that displays messages according to en-US localization rules. 25085 * These rules are bundled with angular.js, but can be overridden 25086 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive 25087 * by specifying the mappings between 25088 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) 25089 * and the strings to be displayed. 25090 * 25091 * # Plural categories and explicit number rules 25092 * There are two 25093 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) 25094 * in Angular's default en-US locale: "one" and "other". 25095 * 25096 * While a plural category may match many numbers (for example, in en-US locale, "other" can match 25097 * any number that is not 1), an explicit number rule can only match one number. For example, the 25098 * explicit number rule for "3" matches the number 3. There are examples of plural categories 25099 * and explicit number rules throughout the rest of this documentation. 25100 * 25101 * # Configuring ngPluralize 25102 * You configure ngPluralize by providing 2 attributes: `count` and `when`. 25103 * You can also provide an optional attribute, `offset`. 25104 * 25105 * The value of the `count` attribute can be either a string or an {@link guide/expression 25106 * Angular expression}; these are evaluated on the current scope for its bound value. 25107 * 25108 * The `when` attribute specifies the mappings between plural categories and the actual 25109 * string to be displayed. The value of the attribute should be a JSON object. 25110 * 25111 * The following example shows how to configure ngPluralize: 25112 * 25113 * ```html 25114 * <ng-pluralize count="personCount"
25115 when="{'0': 'Nobody is viewing.', 25116 * 'one': '1 person is viewing.', 25117 * 'other': '{} people are viewing.'}"> 25118 * </ng-pluralize> 25119 *``` 25120 * 25121 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not 25122 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" 25123 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for 25124 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can 25125 * show "a dozen people are viewing". 25126 * 25127 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted 25128 * into pluralized strings. In the previous example, Angular will replace `{}` with 25129 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder 25130 * for <span ng-non-bindable>{{numberExpression}}</span>. 25131 * 25132 * # Configuring ngPluralize with offset 25133 * The `offset` attribute allows further customization of pluralized text, which can result in 25134 * a better user experience. For example, instead of the message "4 people are viewing this document", 25135 * you might display "John, Kate and 2 others are viewing this document". 25136 * The offset attribute allows you to offset a number by any desired value. 25137 * Let's take a look at an example: 25138 * 25139 * ```html 25140 * <ng-pluralize count="personCount" offset=2 25141 * when="{'0': 'Nobody is viewing.', 25142 * '1': '{{person1}} is viewing.', 25143 * '2': '{{person1}} and {{person2}} are viewing.', 25144 * 'one': '{{person1}}, {{person2}} and one other person are viewing.', 25145 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> 25146 * </ng-pluralize> 25147 * ``` 25148 * 25149 * Notice that we are still using two plural categories(one, other), but we added 25150 * three explicit number rules 0, 1 and 2. 25151 * When one person, perhaps John, views the document, "John is viewing" will be shown. 25152 * When three people view the document, no explicit number rule is found, so 25153 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. 25154 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" 25155 * is shown. 25156 * 25157 * Note that when you specify offsets, you must provide explicit number rules for 25158 * numbers from 0 up to and including the offset. If you use an offset of 3, for example, 25159 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for 25160 * plural categories "one" and "other". 25161 * 25162 * @param {string|expression} count The variable to be bound to. 25163 * @param {string} when The mapping between plural category to its corresponding strings. 25164 * @param {number=} offset Offset to deduct from the total number. 25165 * 25166 * @example 25167 <example module="pluralizeExample"> 25168 <file name="index.html"> 25169 <script> 25170 angular.module('pluralizeExample', []) 25171 .controller('ExampleController', ['$scope', function($scope) { 25172 $scope.person1 = 'Igor'; 25173 $scope.person2 = 'Misko'; 25174 $scope.personCount = 1; 25175 }]); 25176 </script>
25177 <div ng-controller="ExampleController"> 25178 Person 1:<input type="text" ng-model="person1" value="Igor" /><br/> 25179 Person 2:<input type="text" ng-model="person2" value="Misko" /><br/> 25180 Number of People:<input type="text" ng-model="personCount" value="1" /><br/> 25181 25182 <!--- Example with simple pluralization rules for en locale ---> 25183 Without Offset: 25184 <ng-pluralize count="personCount" 25185 when="{'0': 'Nobody is viewing.', 25186 'one': '1 person is viewing.', 25187 'other': '{} people are viewing.'}"> 25188 </ng-pluralize><br> 25189 25190 <!--- Example with offset ---> 25191 With Offset(2): 25192 <ng-pluralize count="personCount" offset=2 25193 when="{'0': 'Nobody is viewing.', 25194 '1': '{{person1}} is viewing.', 25195 '2': '{{person1}} and {{person2}} are viewing.', 25196 'one': '{{person1}}, {{person2}} and one other person are viewing.', 25197 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> 25198 </ng-pluralize> 25199 </div> 25200 </file> 25201 <file name="protractor.js" type="protractor"> 25202 it('should show correct pluralized string', function() { 25203 var withoutOffset = element.all(by.css('ng-pluralize')).get(0); 25204 var withOffset = element.all(by.css('ng-pluralize')).get(1); 25205 var countInput = element(by.model('personCount')); 25206 25207 expect(withoutOffset.getText()).toEqual('1 person is viewing.'); 25208 expect(withOffset.getText()).toEqual('Igor is viewing.'); 25209 25210 countInput.clear(); 25211 countInput.sendKeys('0'); 25212 25213 expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); 25214 expect(withOffset.getText()).toEqual('Nobody is viewing.'); 25215 25216 countInput.clear(); 25217 countInput.sendKeys('2'); 25218 25219 expect(withoutOffset.getText()).toEqual('2 people are viewing.'); 25220 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); 25221 25222 countInput.clear(); 25223 countInput.sendKeys('3'); 25224 25225 expect(withoutOffset.getText()).toEqual('3 people are viewing.'); 25226 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); 25227 25228 countInput.clear(); 25229 countInput.sendKeys('4'); 25230 25231 expect(withoutOffset.getText()).toEqual('4 people are viewing.'); 25232 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); 25233 }); 25234 it('should show data-bound names', function() { 25235 var withOffset = element.all(by.css('ng-pluralize')).get(1); 25236 var personCount = element(by.model('personCount')); 25237 var person1 = element(by.model('person1')); 25238 var person2 = element(by.model('person2')); 25239 personCount.clear(); 25240 personCount.sendKeys('4'); 25241 person1.clear(); 25242 person1.sendKeys('Di'); 25243 person2.clear(); 25244 person2.sendKeys('Vojta'); 25245 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); 25246 }); 25247 </file> 25248 </example> 25249 */ 25250var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { 25251 var BRACE = /{}/g; 25252 return { 25253 restrict: 'EA', 25254 link: function(scope, element, attr) { 25255 var numberExp = attr.count, 25256 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs 25257 offset = attr.offset || 0, 25258 whens = scope.$eval(whenExp) || {}, 25259 whensExpFns = {}, 25260 startSymbol = $interpolate.startSymbol(), 25261 endSymbol = $interpolate.endSymbol(), 25262 isWhen = /^when(Minus)?(.+)$/; 25263 25264 forEach(attr, function(expression, attributeName) { 25265 if (isWhen.test(attributeName)) { 25266 whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] = 25267 element.attr(attr.$attr[attributeName]); 25268 } 25269 });
25270 forEach(whens, function(expression, key) { 25271 whensExpFns[key] = 25272 $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + 25273 offset + endSymbol)); 25274 }); 25275 25276 scope.$watch(function ngPluralizeWatch() { 25277 var value = parseFloat(scope.$eval(numberExp)); 25278 25279 if (!isNaN(value)) { 25280 //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, 25281 //check it against pluralization rules in $locale service 25282 if (!(value in whens)) value = $locale.pluralCat(value - offset); 25283 return whensExpFns[value](scope, element, true); 25284 } else { 25285 return ''; 25286 } 25287 }, function ngPluralizeWatchAction(newVal) { 25288 element.text(newVal); 25289 }); 25290 } 25291 }; 25292}]; 25293 25294/** 25295 * @ngdoc directive 25296 * @name ngRepeat 25297 * 25298 * @description 25299 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template 25300 * instance gets its own scope, where the given loop variable is set to the current collection item, 25301 * and `$index` is set to the item index or key. 25302 * 25303 * Special properties are exposed on the local scope of each template instance, including: 25304 * 25305 * | Variable | Type | Details | 25306 * |-----------|-----------------|-----------------------------------------------------------------------------| 25307 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | 25308 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | 25309 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | 25310 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | 25311 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | 25312 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | 25313 * 25314 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. 25315 * This may be useful when, for instance, nesting ngRepeats. 25316 * 25317 * # Special repeat start and end points 25318 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending 25319 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. 25320 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) 25321 * up to and including the ending HTML tag where **ng-repeat-end** is placed. 25322 * 25323 * The example below makes use of this feature: 25324 * ```html 25325 * <header ng-repeat-start="item in items"> 25326 * Header {{ item }} 25327 * </header> 25328 * <div class="body"> 25329 * Body {{ item }} 25330 * </div> 25331 * <footer ng-repeat-end> 25332 * Footer {{ item }} 25333 * </footer> 25334 * ``` 25335 * 25336 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: 25337 * ```html 25338 * <header> 25339 * Header A 25340 * </header> 25341 * <div class="body"> 25342 * Body A 25343 * </div> 25344 * <footer> 25345 * Footer A 25346 * </footer> 25347 * <header> 25348 * Header B 25349 * </header> 25350 * <div class="body"> 25351 * Body B 25352 * </div> 25353 * <footer> 25354 * Footer B 25355 * </footer> 25356 * ``` 25357 * 25358 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such 25359 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). 25360 * 25361 * @animations 25362 * **.enter** - when a new item is added to the list or when an item is revealed after a filter 25363 * 25364 * **.leave** - when an item is removed from the list or when an item is filtered out 25365 * 25366 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered 25367 * 25368 * @element ANY 25369 * @scope 25370 * @priority 1000 25371 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These 25372 * formats are currently supported: 25373 * 25374 * * `variable in expression` â where variable is the user defined loop variable and `expression` 25375 * is a scope expression giving the collection to enumerate. 25376 * 25377 * For example: `album in artist.albums`. 25378 * 25379 * * `(key, value) in expression` â where `key` and `value` can be any user defined identifiers, 25380 * and `expression` is the scope expression giving the collection to enumerate. 25381 * 25382 * For example: `(name, age) in {'adam':10, 'amalie':12}`. 25383 * 25384 * * `variable in expression track by tracking_expression` â You can also provide an optional tracking function 25385 * which can be used to associate the objects in the collection with the DOM elements. If no tracking function 25386 * is specified the ng-repeat associates elements by identity in the collection. It is an error to have 25387 * more than one tracking function to resolve to the same key. (This would mean that two distinct objects are 25388 * mapped to the same DOM element, which is not possible.) Filters should be applied to the expression, 25389 * before specifying a tracking expression. 25390 * 25391 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements 25392 * will be associated by item identity in the array. 25393 * 25394 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique 25395 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements 25396 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM 25397 * element in the same way in the DOM. 25398 * 25399 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this 25400 * case the object identity does not matter. Two objects are considered equivalent as long as their `id` 25401 * property is same. 25402 * 25403 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter 25404 * to items in conjunction with a tracking expression. 25405 * 25406 * @example 25407 * This example initializes the scope to a list of names and 25408 * then uses `ngRepeat` to display every person: 25409 <example module="ngAnimate" deps="angular-animate.js" animations="true"> 25410 <file name="index.html"> 25411 <div ng-init="friends = [ 25412 {name:'John', age:25, gender:'boy'}, 25413 {name:'Jessie', age:30, gender:'girl'}, 25414 {name:'Johanna', age:28, gender:'girl'}, 25415 {name:'Joy', age:15, gender:'girl'}, 25416 {name:'Mary', age:28, gender:'girl'}, 25417 {name:'Peter', age:95, gender:'boy'}, 25418 {name:'Sebastian', age:50, gender:'boy'}, 25419 {name:'Erika', age:27, gender:'girl'}, 25420 {name:'Patrick', age:40, gender:'boy'}, 25421 {name:'Samantha', age:60, gender:'girl'} 25422 ]"> 25423 I have {{friends.length}} friends. They are: 25424 <input type="search" ng-model="q" placeholder="filter friends..." /> 25425 <ul class="example-animate-container"> 25426 <li class="animate-repeat" ng-repeat="friend in friends | filter:q"> 25427 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. 25428 </li> 25429 </ul> 25430 </div> 25431 </file> 25432 <file name="animations.css"> 25433 .example-animate-container { 25434 background:white; 25435 border:1px solid black; 25436 list-style:none; 25437 margin:0; 25438 padding:0 10px; 25439 } 25440 25441 .animate-repeat { 25442 line-height:40px; 25443 list-style:none; 25444 box-sizing:border-box; 25445 } 25446 25447 .animate-repeat.ng-move, 25448 .animate-repeat.ng-enter, 25449 .animate-repeat.ng-leave { 25450 -webkit-transition:all linear 0.5s; 25451 transition:all linear 0.5s; 25452 } 25453 25454 .animate-repeat.ng-leave.ng-leave-active, 25455 .animate-repeat.ng-move, 25456 .animate-repeat.ng-enter { 25457 opacity:0; 25458 max-height:0; 25459 } 25460 25461 .animate-repeat.ng-leave,
25462 .animate-repeat.ng-move.ng-move-active, 25463 .animate-repeat.ng-enter.ng-enter-active { 25464 opacity:1; 25465 max-height:40px; 25466 } 25467 </file> 25468 <file name="protractor.js" type="protractor"> 25469 var friends = element.all(by.repeater('friend in friends')); 25470 25471 it('should render initial data set', function() { 25472 expect(friends.count()).toBe(10); 25473 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); 25474 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); 25475 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); 25476 expect(element(by.binding('friends.length')).getText()) 25477 .toMatch("I have 10 friends. They are:"); 25478 }); 25479 25480 it('should update repeater when filter predicate changes', function() { 25481 expect(friends.count()).toBe(10); 25482 25483 element(by.model('q')).sendKeys('ma'); 25484 25485 expect(friends.count()).toBe(2); 25486 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); 25487 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); 25488 }); 25489 </file> 25490 </example> 25491 */ 25492var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { 25493 var NG_REMOVED = '$$NG_REMOVED'; 25494 var ngRepeatMinErr = minErr('ngRepeat'); 25495 return { 25496 transclude: 'element', 25497 priority: 1000, 25498 terminal: true, 25499 $$tlb: true, 25500 link: function($scope, $element, $attr, ctrl, $transclude){ 25501 var expression = $attr.ngRepeat; 25502 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), 25503 trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, 25504 lhs, rhs, valueIdentifier, keyIdentifier, 25505 hashFnLocals = {$id: hashKey}; 25506 25507 if (!match) { 25508 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", 25509 expression); 25510 } 25511 25512 lhs = match[1]; 25513 rhs = match[2]; 25514 trackByExp = match[3]; 25515 25516 if (trackByExp) { 25517 trackByExpGetter = $parse(trackByExp); 25518 trackByIdExpFn = function(key, value, index) { 25519 // assign key, value, and $index to the locals so that they can be used in hash functions 25520 if (keyIdentifier) hashFnLocals[keyIdentifier] = key; 25521 hashFnLocals[valueIdentifier] = value; 25522 hashFnLocals.$index = index; 25523 return trackByExpGetter($scope, hashFnLocals); 25524 }; 25525 } else { 25526 trackByIdArrayFn = function(key, value) { 25527 return hashKey(value); 25528 }; 25529 trackByIdObjFn = function(key) { 25530 return key; 25531 }; 25532 } 25533 25534 match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); 25535 if (!match) { 25536 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", 25537 lhs); 25538 } 25539 valueIdentifier = match[3] || match[1]; 25540 keyIdentifier = match[2]; 25541 25542 // Store a list of elements from previous run. This is a hash where key is the item from the 25543 // iterator, and the value is objects with following properties. 25544 // - scope: bound scope 25545 // - element: previous element. 25546 // - index: position 25547 var lastBlockMap = {}; 25548 25549 //watch props 25550 $scope.$watchCollection(rhs, function ngRepeatAction(collection){ 25551 var index, length, 25552 previousNode = $element[0], // current position of the node 25553 nextNode, 25554 // Same as lastBlockMap but it has the current state. It will become the 25555 // lastBlockMap on the next iteration. 25556 nextBlockMap = {}, 25557 arrayLength, 25558 childScope, 25559 key, value, // key/value of iteration 25560 trackById, 25561 trackByIdFn, 25562 collectionKeys, 25563 block, // last object information {scope, element, id} 25564 nextBlockOrder = [], 25565 elementsToRemove; 25566 25567 25568 if (isArrayLike(collection)) { 25569 collectionKeys = collection; 25570 trackByIdFn = trackByIdExpFn || trackByIdArrayFn; 25571 } else { 25572 trackByIdFn = trackByIdExpFn || trackByIdObjFn; 25573 // if object, extract keys, sort them and use to determine order of iteration over obj props 25574 collectionKeys = []; 25575 for (key in collection) { 25576 if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { 25577 collectionKeys.push(key); 25578 } 25579 } 25580 collectionKeys.sort(); 25581 } 25582 25583 arrayLength = collectionKeys.length; 25584 25585 // locate existing items 25586 length = nextBlockOrder.length = collectionKeys.length; 25587 for(index = 0; index < length; index++) { 25588 key = (collection === collectionKeys) ? index : collectionKeys[index]; 25589 value = collection[key]; 25590 trackById = trackByIdFn(key, value, index); 25591 assertNotHasOwnProperty(trackById, '`track by` id'); 25592 if(lastBlockMap.hasOwnProperty(trackById)) { 25593 block = lastBlockMap[trackById]; 25594 delete lastBlockMap[trackById]; 25595 nextBlockMap[trackById] = block; 25596 nextBlockOrder[index] = block; 25597 } else if (nextBlockMap.hasOwnProperty(trackById)) { 25598 // restore lastBlockMap
25599 forEach(nextBlockOrder, function(block) { 25600 if (block && block.scope) lastBlockMap[block.id] = block; 25601 }); 25602 // This is a duplicate and we need to throw an error 25603 throw ngRepeatMinErr('dupes', 25604 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", 25605 expression, trackById, toJson(value)); 25606 } else { 25607 // new never before seen block 25608 nextBlockOrder[index] = { id: trackById }; 25609 nextBlockMap[trackById] = false; 25610 } 25611 } 25612 25613 // remove existing items 25614 for (key in lastBlockMap) { 25615 // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn 25616 if (lastBlockMap.hasOwnProperty(key)) { 25617 block = lastBlockMap[key]; 25618 elementsToRemove = getBlockElements(block.clone); 25619 $animate.leave(elementsToRemove); 25620 forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; }); 25621 block.scope.$destroy(); 25622 } 25623 } 25624 25625 // we are not using forEach for perf reasons (trying to avoid #call) 25626 for (index = 0, length = collectionKeys.length; index < length; index++) { 25627 key = (collection === collectionKeys) ? index : collectionKeys[index]; 25628 value = collection[key]; 25629 block = nextBlockOrder[index]; 25630 if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]); 25631 25632 if (block.scope) { 25633 // if we have already seen this object, then we need to reuse the 25634 // associated scope/element 25635 childScope = block.scope; 25636 25637 nextNode = previousNode; 25638 do { 25639 nextNode = nextNode.nextSibling; 25640 } while(nextNode && nextNode[NG_REMOVED]); 25641 25642 if (getBlockStart(block) != nextNode) { 25643 // existing item which got moved 25644 $animate.move(getBlockElements(block.clone), null, jqLite(previousNode)); 25645 } 25646 previousNode = getBlockEnd(block); 25647 } else { 25648 // new item which we don't know about 25649 childScope = $scope.$new(); 25650 } 25651 25652 childScope[valueIdentifier] = value; 25653 if (keyIdentifier) childScope[keyIdentifier] = key; 25654 childScope.$index = index; 25655 childScope.$first = (index === 0); 25656 childScope.$last = (index === (arrayLength - 1)); 25657 childScope.$middle = !(childScope.$first || childScope.$last); 25658 // jshint bitwise: false 25659 childScope.$odd = !(childScope.$even = (index&1) === 0); 25660 // jshint bitwise: true 25661 25662 if (!block.scope) { 25663 $transclude(childScope, function(clone) { 25664 clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); 25665 $animate.enter(clone, null, jqLite(previousNode)); 25666 previousNode = clone; 25667 block.scope = childScope; 25668 // Note: We only need the first/last node of the cloned nodes. 25669 // However, we need to keep the reference to the jqlite wrapper as it might be changed later 25670 // by a directive with templateUrl when its template arrives. 25671 block.clone = clone; 25672 nextBlockMap[block.id] = block; 25673 }); 25674 } 25675 } 25676 lastBlockMap = nextBlockMap; 25677 }); 25678 } 25679 }; 25680 25681 function getBlockStart(block) { 25682 return block.clone[0]; 25683 } 25684 25685 function getBlockEnd(block) { 25686 return block.clone[block.clone.length - 1]; 25687 } 25688}]; 25689 25690/** 25691 * @ngdoc directive 25692 * @name ngShow 25693 * 25694 * @description 25695 * The `ngShow` directive shows or hides the given HTML element based on the expression 25696 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding 25697 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined 25698 * in AngularJS and sets the display style to none (using an !important flag). 25699 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). 25700 * 25701 * ```html 25702 * <!-- when $scope.myValue is truthy (element is visible) --> 25703 * <div ng-show="myValue"></div> 25704 * 25705 * <!-- when $scope.myValue is falsy (element is hidden) --> 25706 * <div ng-show="myValue" class="ng-hide"></div> 25707 * ``` 25708 * 25709 * When the `ngShow` expression evaluates to false then the `.ng-hide` CSS class is added to the class attribute 25710 * on the element causing it to become hidden. When true, the `.ng-hide` CSS class is removed 25711 * from the element causing the element not to appear hidden. 25712 * 25713 * <div class="alert alert-warning"> 25714 * **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br /> 25715 * "f" / "0" / "false" / "no" / "n" / "[]" 25716 * </div> 25717 * 25718 * ## Why is !important used? 25719 * 25720 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector 25721 * can be easily overridden by heavier selectors. For example, something as simple 25722 * as changing the display style on a HTML list item would make hidden elements appear visible.
25723 * This also becomes a bigger issue when dealing with CSS frameworks. 25724 * 25725 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector 25726 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the 25727 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. 25728 * 25729 * ### Overriding `.ng-hide` 25730 * 25731 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change 25732 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` 25733 * class in CSS: 25734 * 25735 * ```css 25736 * .ng-hide { 25737 * //this is just another form of hiding an element 25738 * display:block!important; 25739 * position:absolute; 25740 * top:-9999px; 25741 * left:-9999px; 25742 * } 25743 * ``` 25744 * 25745 * By default you don't need to override in CSS anything and the animations will work around the display style. 25746 * 25747 * ## A note about animations with `ngShow` 25748 * 25749 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression 25750 * is true and false. This system works like the animation system present with ngClass except that 25751 * you must also include the !important flag to override the display property 25752 * so that you can perform an animation when the element is hidden during the time of the animation. 25753 * 25754 * ```css 25755 * // 25756 * //a working example can be found at the bottom of this page 25757 * // 25758 * .my-element.ng-hide-add, .my-element.ng-hide-remove { 25759 * transition:0.5s linear all; 25760 * } 25761 * 25762 * .my-element.ng-hide-add { ... } 25763 * .my-element.ng-hide-add.ng-hide-add-active { ... } 25764 * .my-element.ng-hide-remove { ... } 25765 * .my-element.ng-hide-remove.ng-hide-remove-active { ... } 25766 * ``` 25767 * 25768 * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display 25769 * property to block during animation states--ngAnimate will handle the style toggling automatically for you. 25770 * 25771 * @animations 25772 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible 25773 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden 25774 * 25775 * @element ANY 25776 * @param {expression} ngShow If the {@link guide/expression expression} is truthy 25777 * then the element is shown or hidden respectively. 25778 * 25779 * @example 25780 <example module="ngAnimate" deps="angular-animate.js" animations="true"> 25781 <file name="index.html"> 25782 Click me: <input type="checkbox" ng-model="checked"><br/> 25783 <div> 25784 Show: 25785 <div class="check-element animate-show" ng-show="checked"> 25786 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. 25787 </div> 25788 </div> 25789 <div> 25790 Hide: 25791 <div class="check-element animate-show" ng-hide="checked"> 25792 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. 25793 </div> 25794 </div> 25795 </file> 25796 <file name="glyphicons.css"> 25797 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); 25798 </file> 25799 <file name="animations.css"> 25800 .animate-show { 25801 -webkit-transition:all linear 0.5s; 25802 transition:all linear 0.5s; 25803 line-height:20px; 25804 opacity:1; 25805 padding:10px; 25806 border:1px solid black; 25807 background:white; 25808 } 25809 25810 .animate-show.ng-hide { 25811 line-height:0; 25812 opacity:0; 25813 padding:0 10px; 25814 } 25815 25816 .check-element { 25817 padding:10px; 25818 border:1px solid black; 25819 background:white; 25820 } 25821 </file> 25822 <file name="protractor.js" type="protractor"> 25823 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); 25824 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); 25825 25826 it('should check ng-show / ng-hide', function() { 25827 expect(thumbsUp.isDisplayed()).toBeFalsy(); 25828 expect(thumbsDown.isDisplayed()).toBeTruthy(); 25829
25830 element(by.model('checked')).click(); 25831 25832 expect(thumbsUp.isDisplayed()).toBeTruthy(); 25833 expect(thumbsDown.isDisplayed()).toBeFalsy(); 25834 }); 25835 </file> 25836 </example> 25837 */ 25838var ngShowDirective = ['$animate', function($animate) { 25839 return function(scope, element, attr) { 25840 scope.$watch(attr.ngShow, function ngShowWatchAction(value){ 25841 $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide'); 25842 }); 25843 }; 25844}]; 25845 25846 25847/** 25848 * @ngdoc directive 25849 * @name ngHide 25850 * 25851 * @description 25852 * The `ngHide` directive shows or hides the given HTML element based on the expression 25853 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding 25854 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined 25855 * in AngularJS and sets the display style to none (using an !important flag). 25856 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). 25857 * 25858 * ```html 25859 * <!-- when $scope.myValue is truthy (element is hidden) --> 25860 * <div ng-hide="myValue" class="ng-hide"></div> 25861 * 25862 * <!-- when $scope.myValue is falsy (element is visible) --> 25863 * <div ng-hide="myValue"></div> 25864 * ``` 25865 * 25866 * When the `.ngHide` expression evaluates to true then the `.ng-hide` CSS class is added to the class attribute 25867 * on the element causing it to become hidden. When false, the `.ng-hide` CSS class is removed 25868 * from the element causing the element not to appear hidden. 25869 * 25870 * <div class="alert alert-warning"> 25871 * **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br /> 25872 * "f" / "0" / "false" / "no" / "n" / "[]" 25873 * </div> 25874 * 25875 * ## Why is !important used? 25876 * 25877 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector 25878 * can be easily overridden by heavier selectors. For example, something as simple 25879 * as changing the display style on a HTML list item would make hidden elements appear visible. 25880 * This also becomes a bigger issue when dealing with CSS frameworks. 25881 * 25882 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector 25883 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the 25884 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. 25885 * 25886 * ### Overriding `.ng-hide` 25887 * 25888 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change 25889 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` 25890 * class in CSS: 25891 * 25892 * ```css 25893 * .ng-hide { 25894 * //this is just another form of hiding an element 25895 * display:block!important; 25896 * position:absolute; 25897 * top:-9999px; 25898 * left:-9999px; 25899 * } 25900 * ``` 25901 * 25902 * By default you don't need to override in CSS anything and the animations will work around the display style. 25903 * 25904 * ## A note about animations with `ngHide` 25905 * 25906 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression 25907 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` 25908 * CSS class is added and removed for you instead of your own CSS class. 25909 * 25910 * ```css 25911 * // 25912 * //a working example can be found at the bottom of this page 25913 * // 25914 * .my-element.ng-hide-add, .my-element.ng-hide-remove { 25915 * transition:0.5s linear all; 25916 * } 25917 * 25918 * .my-element.ng-hide-add { ... } 25919 * .my-element.ng-hide-add.ng-hide-add-active { ... } 25920 * .my-element.ng-hide-remove { ... } 25921 * .my-element.ng-hide-remove.ng-hide-remove-active { ... } 25922 * ``` 25923 * 25924 * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display 25925 * property to block during animation states--ngAnimate will handle the style toggling automatically for you. 25926 * 25927 * @animations 25928 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden 25929 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible 25930 * 25931 * @element ANY 25932 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then 25933 * the element is shown or hidden respectively. 25934 * 25935 * @example 25936 <example module="ngAnimate" deps="angular-animate.js" animations="true"> 25937 <file name="index.html"> 25938 Click me: <input type="checkbox" ng-model="checked"><br/> 25939 <div> 25940 Show: 25941 <div class="check-element animate-hide" ng-show="checked">
25942 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. 25943 </div> 25944 </div> 25945 <div> 25946 Hide: 25947 <div class="check-element animate-hide" ng-hide="checked"> 25948 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. 25949 </div> 25950 </div> 25951 </file> 25952 <file name="glyphicons.css"> 25953 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); 25954 </file> 25955 <file name="animations.css"> 25956 .animate-hide { 25957 -webkit-transition:all linear 0.5s; 25958 transition:all linear 0.5s; 25959 line-height:20px; 25960 opacity:1; 25961 padding:10px; 25962 border:1px solid black; 25963 background:white; 25964 } 25965 25966 .animate-hide.ng-hide { 25967 line-height:0; 25968 opacity:0; 25969 padding:0 10px; 25970 } 25971 25972 .check-element { 25973 padding:10px; 25974 border:1px solid black; 25975 background:white; 25976 } 25977 </file> 25978 <file name="protractor.js" type="protractor"> 25979 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); 25980 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); 25981 25982 it('should check ng-show / ng-hide', function() { 25983 expect(thumbsUp.isDisplayed()).toBeFalsy(); 25984 expect(thumbsDown.isDisplayed()).toBeTruthy(); 25985 25986 element(by.model('checked')).click(); 25987 25988 expect(thumbsUp.isDisplayed()).toBeTruthy(); 25989 expect(thumbsDown.isDisplayed()).toBeFalsy(); 25990 }); 25991 </file> 25992 </example> 25993 */ 25994var ngHideDirective = ['$animate', function($animate) { 25995 return function(scope, element, attr) { 25996 scope.$watch(attr.ngHide, function ngHideWatchAction(value){ 25997 $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide'); 25998 }); 25999 }; 26000}]; 26001 26002/** 26003 * @ngdoc directive 26004 * @name ngStyle 26005 * @restrict AC 26006 * 26007 * @description 26008 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. 26009 * 26010 * @element ANY 26011 * @param {expression} ngStyle 26012 * 26013 * {@link guide/expression Expression} which evals to an 26014 * object whose keys are CSS style names and values are corresponding values for those CSS 26015 * keys. 26016 * 26017 * Since some CSS style names are not valid keys for an object, they must be quoted. 26018 * See the 'background-color' style in the example below. 26019 * 26020 * @example 26021 <example> 26022 <file name="index.html"> 26023 <input type="button" value="set color" ng-click="myStyle={color:'red'}"> 26024 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}"> 26025 <input type="button" value="clear" ng-click="myStyle={}"> 26026 <br/> 26027 <span ng-style="myStyle">Sample Text</span> 26028 <pre>myStyle={{myStyle}}</pre> 26029 </file> 26030 <file name="style.css"> 26031 span { 26032 color: black; 26033 } 26034 </file> 26035 <file name="protractor.js" type="protractor"> 26036 var colorSpan = element(by.css('span')); 26037 26038 it('should check ng-style', function() { 26039 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); 26040 element(by.css('input[value=\'set color\']')).click(); 26041 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); 26042 element(by.css('input[value=clear]')).click(); 26043 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); 26044 }); 26045 </file> 26046 </example> 26047 */ 26048var ngStyleDirective = ngDirective(function(scope, element, attr) { 26049 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { 26050 if (oldStyles && (newStyles !== oldStyles)) { 26051 forEach(oldStyles, function(val, style) { element.css(style, '');}); 26052 } 26053 if (newStyles) element.css(newStyles); 26054 }, true); 26055}); 26056 26057/** 26058 * @ngdoc directive 26059 * @name ngSwitch 26060 * @restrict EA 26061 * 26062 * @description 26063 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. 26064 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location 26065 * as specified in the template. 26066 * 26067 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it 26068 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element 26069 * matches the value obtained from the evaluated expression. In other words, you define a container element 26070 * (where you place the directive), place an expression on the **`on="..."` attribute** 26071 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place 26072 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on 26073 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default 26074 * attribute is displayed. 26075 * 26076 * <div class="alert alert-info"> 26077 * Be aware that the attribute values to match against cannot be expressions. They are interpreted 26078 * as literal string values to match against. 26079 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
26079omeVal"` not against the 26080 * value of the expression `$scope.someVal`. 26081 * </div> 26082 26083 * @animations 26084 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container 26085 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM 26086 * 26087 * @usage 26088 * 26089 * ``` 26090 * <ANY ng-switch="expression"> 26091 * <ANY ng-switch-when="matchValue1">...</ANY> 26092 * <ANY ng-switch-when="matchValue2">...</ANY> 26093 * <ANY ng-switch-default>...</ANY> 26094 * </ANY> 26095 * ``` 26096 * 26097 * 26098 * @scope 26099 * @priority 800 26100 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>. 26101 * On child elements add: 26102 * 26103 * * `ngSwitchWhen`: the case statement to match against. If match then this 26104 * case will be displayed. If the same match appears multiple times, all the 26105 * elements will be displayed. 26106 * * `ngSwitchDefault`: the default case when no other case match. If there 26107 * are multiple default cases, all of them will be displayed when no other 26108 * case match. 26109 * 26110 * 26111 * @example 26112 <example module="switchExample" deps="angular-animate.js" animations="true"> 26113 <file name="index.html">
26114 <div ng-controller="ExampleController"> 26115 <select ng-model="selection" ng-options="item for item in items"> 26116 </select> 26117 <tt>selection={{selection}}</tt> 26118 <hr/> 26119 <div class="animate-switch-container" 26120 ng-switch on="selection"> 26121 <div class="animate-switch" ng-switch-when="settings">Settings Div</div> 26122 <div class="animate-switch" ng-switch-when="home">Home Span</div> 26123 <div class="animate-switch" ng-switch-default>default</div> 26124 </div> 26125 </div> 26126 </file> 26127 <file name="script.js"> 26128 angular.module('switchExample', ['ngAnimate']) 26129 .controller('ExampleController', ['$scope', function($scope) { 26130 $scope.items = ['settings', 'home', 'other']; 26131 $scope.selection = $scope.items[0]; 26132 }]); 26133 </file> 26134 <file name="animations.css"> 26135 .animate-switch-container { 26136 position:relative; 26137 background:white; 26138 border:1px solid black; 26139 height:40px; 26140 overflow:hidden; 26141 } 26142 26143 .animate-switch { 26144 padding:10px; 26145 } 26146 26147 .animate-switch.ng-animate { 26148 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 26149 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; 26150 26151 position:absolute; 26152 top:0; 26153 left:0; 26154 right:0; 26155 bottom:0; 26156 } 26157 26158 .animate-switch.ng-leave.ng-leave-active, 26159 .animate-switch.ng-enter { 26160 top:-50px; 26161 } 26162 .animate-switch.ng-leave, 26163 .animate-switch.ng-enter.ng-enter-active { 26164 top:0; 26165 } 26166 </file> 26167 <file name="protractor.js" type="protractor"> 26168 var switchElem = element(by.css('[ng-switch]')); 26169 var select = element(by.model('selection')); 26170 26171 it('should start in settings', function() { 26172 expect(switchElem.getText()).toMatch(/Settings Div/); 26173 }); 26174 it('should change to home', function() { 26175 select.all(by.css('option')).get(1).click(); 26176 expect(switchElem.getText()).toMatch(/Home Span/); 26177 }); 26178 it('should select default', function() { 26179 select.all(by.css('option')).get(2).click(); 26180 expect(switchElem.getText()).toMatch(/default/); 26181 }); 26182 </file> 26183 </example> 26184 */ 26185var ngSwitchDirective = ['$animate', function($animate) { 26186 return { 26187 restrict: 'EA', 26188 require: 'ngSwitch', 26189 26190 // asks for $scope to fool the BC controller module 26191 controller: ['$scope', function ngSwitchController() { 26192 this.cases = {}; 26193 }], 26194 link: function(scope, element, attr, ngSwitchController) { 26195 var watchExpr = attr.ngSwitch || attr.on, 26196 selectedTranscludes = [], 26197 selectedElements = [], 26198 previousElements = [], 26199 selectedScopes = []; 26200 26201 scope.$watch(watchExpr, function ngSwitchWatchAction(value) { 26202 var i, ii; 26203 for (i = 0, ii = previousElements.length; i < ii; ++i) { 26204 previousElements[i].remove(); 26205 } 26206 previousElements.length = 0; 26207 26208 for (i = 0, ii = selectedScopes.length; i < ii; ++i) { 26209 var selected = selectedElements[i]; 26210 selectedScopes[i].$destroy(); 26211 previousElements[i] = selected; 26212 $animate.leave(selected, function() { 26213 previousElements.splice(i, 1); 26214 }); 26215 } 26216 26217 selectedElements.length = 0; 26218 selectedScopes.length = 0; 26219 26220 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { 26221 scope.$eval(attr.change); 26222 forEach(selectedTranscludes, function(selectedTransclude) { 26223 var selectedScope = scope.$new(); 26224 selectedScopes.push(selectedScope); 26225 selectedTransclude.transclude(selectedScope, function(caseElement) { 26226 var anchor = selectedTransclude.element; 26227 26228 selectedElements.push(caseElement); 26229 $animate.enter(caseElement, anchor.parent(), anchor); 26230 }); 26231 }); 26232 } 26233 }); 26234 } 26235 }; 26236}]; 26237 26238var ngSwitchWhenDirective = ngDirective({ 26239 transclude: 'element', 26240 priority: 800, 26241 require: '^ngSwitch', 26242 link: function(scope, element, attrs, ctrl, $transclude) { 26243 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); 26244 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); 26245 } 26246}); 26247 26248var ngSwitchDefaultDirective = ngDirective({ 26249 transclude: 'element', 26250 priority: 800, 26251 require: '^ngSwitch', 26252 link: function(scope, element, attr, ctrl, $transclude) { 26253 ctrl.cases['?'] = (ctrl.cases['?'] || []); 26254 ctrl.cases['?'].push({ transclude: $transclude, element: element }); 26255 } 26256}); 26257 26258/** 26259 * @ngdoc directive 26260 * @name ngTransclude 26261 * @restrict AC 26262 * 26263 * @description 26264 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. 26265 * 26266 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted. 26267 * 26268 * @element ANY 26269 * 26270 * @example 26271 <example module="transcludeExample"> 26272 <file name="index.html"> 26273 <script> 26274 angular.module('transcludeExample', []) 26275 .directive('pane', function(){ 26276 return { 26277 restrict: 'E', 26278 transclude: true, 26279 scope: { title:'@' }, 26280 template: '<div style="border: 1px solid black;">
26280' + 26281 '<div style="background-color: gray">{{title}}</div>' + 26282 '<div ng-transclude></div>' + 26283 '</div>' 26284 }; 26285 }) 26286 .controller('ExampleController', ['$scope', function($scope) { 26287 $scope.title = 'Lorem Ipsum'; 26288 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; 26289 }]); 26290 </script> 26291 <div ng-controller="ExampleController"> 26292 <input ng-model="title"><br> 26293 <textarea ng-model="text"></textarea> <br/> 26294 <pane title="{{title}}">{{text}}</pane> 26295 </div> 26296 </file> 26297 <file name="protractor.js" type="protractor"> 26298 it('should have transcluded', function() { 26299 var titleElement = element(by.model('title')); 26300 titleElement.clear(); 26301 titleElement.sendKeys('TITLE'); 26302 var textElement = element(by.model('text')); 26303 textElement.clear(); 26304 textElement.sendKeys('TEXT'); 26305 expect(element(by.binding('title')).getText()).toEqual('TITLE'); 26306 expect(element(by.binding('text')).getText()).toEqual('TEXT'); 26307 }); 26308 </file> 26309 </example> 26310 * 26311 */ 26312var ngTranscludeDirective = ngDirective({ 26313 link: function($scope, $element, $attrs, controller, $transclude) { 26314 if (!$transclude) { 26315 throw minErr('ngTransclude')('orphan', 26316 'Illegal use of ngTransclude directive in the template! ' + 26317 'No parent directive that requires a transclusion found. ' + 26318 'Element: {0}', 26319 startingTag($element)); 26320 } 26321 26322 $transclude(function(clone) { 26323 $element.empty(); 26324 $element.append(clone); 26325 }); 26326 } 26327}); 26328 26329/** 26330 * @ngdoc directive 26331 * @name script 26332 * @restrict E 26333 * 26334 * @description 26335 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the 26336 * template can be used by {@link ng.directive:ngInclude `ngInclude`}, 26337 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the 26338 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be 26339 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`. 26340 * 26341 * @param {string} type Must be set to `'text/ng-template'`. 26342 * @param {string} id Cache name of the template. 26343 * 26344 * @example 26345 <example> 26346 <file name="index.html"> 26347 <script type="text/ng-template" id="/tpl.html"> 26348 Content of the template. 26349 </script> 26350 26351 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a> 26352 <div id="tpl-content" ng-include src="currentTpl"></div> 26353 </file> 26354 <file name="protractor.js" type="protractor"> 26355 it('should load template defined inside script tag', function() { 26356 element(by.css('#tpl-link')).click(); 26357 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); 26358 }); 26359 </file> 26360 </example> 26361 */ 26362var scriptDirective = ['$templateCache', function($templateCache) { 26363 return { 26364 restrict: 'E', 26365 terminal: true, 26366 compile: function(element, attr) { 26367 if (attr.type == 'text/ng-template') { 26368 var templateUrl = attr.id, 26369 // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent 26370 text = element[0].text; 26371 26372 $templateCache.put(templateUrl, text); 26373 } 26374 } 26375 }; 26376}]; 26377 26378var ngOptionsMinErr = minErr('ngOptions'); 26379/** 26380 * @ngdoc directive 26381 * @name select 26382 * @restrict E 26383 * 26384 * @description 26385 * HTML `SELECT` element with angular data-binding. 26386 * 26387 * # `ngOptions` 26388 * 26389 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` 26390 * elements for the `<select>` element using the array or object obtained by evaluating the 26391 * `ngOptions` comprehension_expression. 26392 * 26393 * When an item in the `<select>` menu is selected, the array element or object property 26394 * represented by the selected option will be bound to the model identified by the `ngModel` 26395 * directive. 26396 * 26397 * <div class="alert alert-warning"> 26398 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an 26399 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). 26400 * </div> 26401 * 26402 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can 26403 * be nested into the `<select>` element. This element will then represent the `null` or "not selected" 26404 * option. See example below for demonstration. 26405 * 26406 * <div class="alert alert-warning"> 26407 * **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead 26408 * of {@link ng.directive:ngRepeat ngRepeat} when you want the 26409 * `select` model to be bound to a non-string value. This is because an option element can only 26410 * be bound to string values at present. 26411 * </div> 26412 * 26413 * @param {string} ngModel Assignable angular expression to data-bind to. 26414 * @param {string=} name Property name of the form under which the control is published. 26415 * @param {string=} required The control is considered valid only if value is entered. 26416 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to 26417 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of 26418 * `required` when you want to data-bind to the `required` attribute. 26419 * @param {comprehension_expression=} ngOptions in one of the following forms: 26420 * 26421 * * for array data sources: 26422 * * `label` **`for`** `value` **`in`** `array` 26423 * * `select` **`as`** `label` **`for`** `value` **`in`** `array` 26424 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` 26425 * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` 26426 * * for object data sources:
26427 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` 26428 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` 26429 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` 26430 * * `select` **`as`** `label` **`group by`** `group` 26431 * **`for` `(`**`key`**`,`** `value`**`) in`** `object` 26432 * 26433 * Where: 26434 * 26435 * * `array` / `object`: an expression which evaluates to an array / object to iterate over. 26436 * * `value`: local variable which will refer to each item in the `array` or each property value 26437 * of `object` during iteration. 26438 * * `key`: local variable which will refer to a property name in `object` during iteration. 26439 * * `label`: The result of this expression will be the label for `<option>` element. The 26440 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). 26441 * * `select`: The result of this expression will be bound to the model of the parent `<select>` 26442 * element. If not specified, `select` expression will default to `value`. 26443 * * `group`: The result of this expression will be used to group options using the `<optgroup>` 26444 * DOM element. 26445 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be 26446 * used to identify the objects in the array. The `trackexpr` will most likely refer to the 26447 * `value` variable (e.g. `value.propertyName`). 26448 * 26449 * @example 26450 <example module="selectExample"> 26451 <file name="index.html"> 26452 <script> 26453 angular.module('selectExample', []) 26454 .controller('ExampleController', ['$scope', function($scope) { 26455 $scope.colors = [ 26456 {name:'black', shade:'dark'}, 26457 {name:'white', shade:'light'}, 26458 {name:'red', shade:'dark'}, 26459 {name:'blue', shade:'dark'}, 26460 {name:'yellow', shade:'light'} 26461 ]; 26462 $scope.myColor = $scope.colors[2]; // red 26463 }]); 26464 </script>
26465 <div ng-controller="ExampleController"> 26466 <ul> 26467 <li ng-repeat="color in colors"> 26468 Name: <input ng-model="color.name"> 26469 [<a href ng-click="colors.splice($index, 1)">X</a>] 26470 </li> 26471 <li> 26472 [<a href ng-click="colors.push({})">add</a>] 26473 </li> 26474 </ul> 26475 <hr/> 26476 Color (null not allowed): 26477 <select ng-model="myColor" ng-options="color.name for color in colors"></select><br> 26478 26479 Color (null allowed): 26480 <span class="nullable"> 26481 <select ng-model="myColor" ng-options="color.name for color in colors"> 26482 <option value="">-- choose color --</option> 26483 </select> 26484 </span><br/> 26485 26486 Color grouped by shade: 26487 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors"> 26488 </select><br/> 26489 26490 26491 Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br> 26492 <hr/> 26493 Currently selected: {{ {selected_color:myColor} }} 26494 <div style="border:solid 1px black; height:20px" 26495 ng-style="{'background-color':myColor.name}"> 26496 </div> 26497 </div> 26498 </file> 26499 <file name="protractor.js" type="protractor"> 26500 it('should check ng-options', function() { 26501 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); 26502 element.all(by.model('myColor')).first().click(); 26503 element.all(by.css('select[ng-model="myColor"] option')).first().click(); 26504 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); 26505 element(by.css('.nullable select[ng-model="myColor"]')).click(); 26506 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); 26507 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); 26508 }); 26509 </file> 26510 </example> 26511 */ 26512 26513var ngOptionsDirective = valueFn({ terminal: true }); 26514// jshint maxlen: false 26515var selectDirective = ['$compile', '$parse', function($compile, $parse) { 26516 //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888 26517 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, 26518 nullModelCtrl = {$setViewValue: noop}; 26519// jshint maxlen: 100 26520 26521 return { 26522 restrict: 'E', 26523 require: ['select', '?ngModel'], 26524 controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { 26525 var self = this, 26526 optionsMap = {}, 26527 ngModelCtrl = nullModelCtrl, 26528 nullOption, 26529 unknownOption; 26530 26531 26532 self.databound = $attrs.ngModel; 26533 26534 26535 self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { 26536 ngModelCtrl = ngModelCtrl_; 26537 nullOption = nullOption_; 26538 unknownOption = unknownOption_; 26539 }; 26540 26541 26542 self.addOption = function(value) { 26543 assertNotHasOwnProperty(value, '"option value"'); 26544 optionsMap[value] = true; 26545 26546 if (ngModelCtrl.$viewValue == value) { 26547 $element.val(value); 26548 if (unknownOption.parent()) unknownOption.remove(); 26549 } 26550 }; 26551 26552 26553 self.removeOption = function(value) { 26554 if (this.hasOption(value)) { 26555 delete optionsMap[value]; 26556 if (ngModelCtrl.$viewValue == value) { 26557 this.renderUnknownOption(value); 26558 } 26559 } 26560 }; 26561 26562 26563 self.renderUnknownOption = function(val) { 26564 var unknownVal = '? ' + hashKey(val) + ' ?'; 26565 unknownOption.val(unknownVal); 26566 $element.prepend(unknownOption); 26567 $element.val(unknownVal); 26568 unknownOption.prop('selected', true); // needed for IE 26569 }; 26570 26571 26572 self.hasOption = function(value) { 26573 return optionsMap.hasOwnProperty(value); 26574 }; 26575 26576 $scope.$on('$destroy', function() { 26577 // disable unknown option so that we don't do work when the whole select is being destroyed 26578 self.renderUnknownOption = noop; 26579 }); 26580 }], 26581 26582 link: function(scope, element, attr, ctrls) { 26583 // if ngModel is not defined, we don't need to do anything 26584 if (!ctrls[1]) return; 26585 26586 var selectCtrl = ctrls[0], 26587 ngModelCtrl = ctrls[1], 26588 multiple = attr.multiple, 26589 optionsExp = attr.ngOptions, 26590 nullOption = false, // if false, user will not be able to select it (used by ngOptions) 26591 emptyOption, 26592 // we can't just jqLite('<option>') since jqLite is not smart enough 26593 // to create it in <select> and IE barfs otherwise. 26594 optionTemplate = jqLite(document.createElement('option')), 26595 optGroupTemplate =jqLite(document.createElement('optgroup')), 26596 unknownOption = optionTemplate.clone(); 26597 26598 // find "null" option 26599 for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) { 26600 if (children[i].value === '') { 26601 emptyOption = nullOption = children.eq(i); 26602 break; 26603 } 26604 } 26605 26606 selectCtrl.init(ngModelCtrl, nullOption, unknownOption); 26607 26608 // required validator 26609 if (multiple) { 26610 ngModelCtrl.$isEmpty = function(value) { 26611 return !value || value.length === 0; 26612 }; 26613 } 26614 26615 if (optionsExp) setupAsOptions(scope, element, ngModelCtrl); 26616 else if (multiple) setupAsMultiple(scope, element, ngModelCtrl); 26617 else setupAsSingle(scope, element, ngModelCtrl, selectCtrl); 26618 26619
26620 //////////////////////////// 26621 26622 26623 26624 function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) { 26625 ngModelCtrl.$render = function() { 26626 var viewValue = ngModelCtrl.$viewValue; 26627 26628 if (selectCtrl.hasOption(viewValue)) { 26629 if (unknownOption.parent()) unknownOption.remove(); 26630 selectElement.val(viewValue); 26631 if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy 26632 } else { 26633 if (isUndefined(viewValue) && emptyOption) { 26634 selectElement.val(''); 26635 } else { 26636 selectCtrl.renderUnknownOption(viewValue); 26637 } 26638 } 26639 }; 26640 26641 selectElement.on('change', function() { 26642 scope.$apply(function() { 26643 if (unknownOption.parent()) unknownOption.remove(); 26644 ngModelCtrl.$setViewValue(selectElement.val()); 26645 }); 26646 }); 26647 } 26648 26649 function setupAsMultiple(scope, selectElement, ctrl) { 26650 var lastView; 26651 ctrl.$render = function() { 26652 var items = new HashMap(ctrl.$viewValue); 26653 forEach(selectElement.find('option'), function(option) { 26654 option.selected = isDefined(items.get(option.value)); 26655 }); 26656 }; 26657 26658 // we have to do it on each watch since ngModel watches reference, but 26659 // we need to work of an array, so we need to see if anything was inserted/removed 26660 scope.$watch(function selectMultipleWatch() { 26661 if (!equals(lastView, ctrl.$viewValue)) { 26662 lastView = shallowCopy(ctrl.$viewValue); 26663 ctrl.$render(); 26664 } 26665 }); 26666 26667 selectElement.on('change', function() { 26668 scope.$apply(function() { 26669 var array = []; 26670 forEach(selectElement.find('option'), function(option) { 26671 if (option.selected) { 26672 array.push(option.value); 26673 } 26674 }); 26675 ctrl.$setViewValue(array); 26676 }); 26677 }); 26678 } 26679 26680 function setupAsOptions(scope, selectElement, ctrl) { 26681 var match; 26682 26683 if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) { 26684 throw ngOptionsMinErr('iexp', 26685 "Expected expression in form of " + 26686 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + 26687 " but got '{0}'. Element: {1}", 26688 optionsExp, startingTag(selectElement)); 26689 } 26690 26691 var displayFn = $parse(match[2] || match[1]), 26692 valueName = match[4] || match[6], 26693 keyName = match[5], 26694 groupByFn = $parse(match[3] || ''), 26695 valueFn = $parse(match[2] ? match[1] : valueName), 26696 valuesFn = $parse(match[7]), 26697 track = match[8], 26698 trackFn = track ? $parse(match[8]) : null, 26699 // This is an array of array of existing option groups in DOM. 26700 // We try to reuse these if possible 26701 // - optionGroupsCache[0] is the options with no option group 26702 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element 26703 optionGroupsCache = [[{element: selectElement, label:''}]]; 26704 26705 if (nullOption) { 26706 // compile the element since there might be bindings in it 26707 $compile(nullOption)(scope); 26708 26709 // remove the class, which is added automatically because we recompile the element and it 26710 // becomes the compilation root 26711 nullOption.removeClass('ng-scope'); 26712 26713 // we need to remove it before calling selectElement.empty() because otherwise IE will 26714 // remove the label from the element. wtf? 26715 nullOption.remove(); 26716 } 26717 26718 // clear contents, we'll add what's needed based on the model 26719 selectElement.empty(); 26720 26721 selectElement.on('change', function() { 26722 scope.$apply(function() { 26723 var optionGroup, 26724 collection = valuesFn(scope) || [], 26725 locals = {}, 26726 key, value, optionElement, index, groupIndex, length, groupLength, trackIndex; 26727 26728 if (multiple) { 26729 value = []; 26730 for (groupIndex = 0, groupLength = optionGroupsCache.length; 26731 groupIndex < groupLength; 26732 groupIndex++) { 26733 // list of options for that group. (first item has the parent) 26734 optionGroup = optionGroupsCache[groupIndex]; 26735 26736 for(index = 1, length = optionGroup.length; index < length; index++) { 26737 if ((optionElement = optionGroup[index].element)[0].selected) { 26738 key = optionElement.val(); 26739 if (keyName) locals[keyName] = key; 26740 if (trackFn) { 26741 for (trackIndex = 0; trackIndex < collection.length; trackIndex++) { 26742 locals[valueName] = collection[trackIndex]; 26743 if (trackFn(scope, locals) == key) break; 26744 } 26745 } else { 26746 locals[valueName] = collection[key]; 26747 } 26748 value.push(valueFn(scope, locals)); 26749 } 26750 } 26751 } 26752 } else { 26753 key = selectElement.val(); 26754 if (key == '?') { 26755 value = undefined; 26756 } else if (key === ''){
26757 value = null; 26758 } else { 26759 if (trackFn) { 26760 for (trackIndex = 0; trackIndex < collection.length; trackIndex++) { 26761 locals[valueName] = collection[trackIndex]; 26762 if (trackFn(scope, locals) == key) { 26763 value = valueFn(scope, locals); 26764 break; 26765 } 26766 } 26767 } else { 26768 locals[valueName] = collection[key]; 26769 if (keyName) locals[keyName] = key; 26770 value = valueFn(scope, locals); 26771 } 26772 } 26773 } 26774 ctrl.$setViewValue(value); 26775 render(); 26776 }); 26777 }); 26778 26779 ctrl.$render = render; 26780 26781 scope.$watchCollection(valuesFn, render); 26782 scope.$watchCollection(function () { 26783 var locals = {}, 26784 values = valuesFn(scope); 26785 if (values) { 26786 var toDisplay = new Array(values.length); 26787 for (var i = 0, ii = values.length; i < ii; i++) { 26788 locals[valueName] = values[i]; 26789 toDisplay[i] = displayFn(scope, locals); 26790 } 26791 return toDisplay; 26792 } 26793 }, render); 26794 26795 if ( multiple ) { 26796 scope.$watchCollection(function() { return ctrl.$modelValue; }, render); 26797 } 26798 26799 function getSelectedSet() { 26800 var selectedSet = false; 26801 if (multiple) { 26802 var modelValue = ctrl.$modelValue; 26803 if (trackFn && isArray(modelValue)) { 26804 selectedSet = new HashMap([]); 26805 var locals = {}; 26806 for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) { 26807 locals[valueName] = modelValue[trackIndex]; 26808 selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]); 26809 } 26810 } else { 26811 selectedSet = new HashMap(modelValue); 26812 } 26813 } 26814 return selectedSet; 26815 } 26816 26817 26818 function render() { 26819 // Temporary location for the option groups before we render them 26820 var optionGroups = {'':[]}, 26821 optionGroupNames = [''], 26822 optionGroupName, 26823 optionGroup, 26824 option, 26825 existingParent, existingOptions, existingOption, 26826 modelValue = ctrl.$modelValue, 26827 values = valuesFn(scope) || [], 26828 keys = keyName ? sortedKeys(values) : values, 26829 key, 26830 groupLength, length, 26831 groupIndex, index, 26832 locals = {}, 26833 selected, 26834 selectedSet = getSelectedSet(), 26835 lastElement, 26836 element, 26837 label; 26838 26839 26840 // We now build up the list of options we need (we merge later) 26841 for (index = 0; length = keys.length, index < length; index++) { 26842 26843 key = index; 26844 if (keyName) { 26845 key = keys[index]; 26846 if ( key.charAt(0) === '$' ) continue; 26847 locals[keyName] = key; 26848 } 26849 26850 locals[valueName] = values[key]; 26851 26852 optionGroupName = groupByFn(scope, locals) || ''; 26853 if (!(optionGroup = optionGroups[optionGroupName])) { 26854 optionGroup = optionGroups[optionGroupName] = []; 26855 optionGroupNames.push(optionGroupName); 26856 } 26857 if (multiple) { 26858 selected = isDefined( 26859 selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals)) 26860 ); 26861 } else { 26862 if (trackFn) { 26863 var modelCast = {}; 26864 modelCast[valueName] = modelValue; 26865 selected = trackFn(scope, modelCast) === trackFn(scope, locals); 26866 } else { 26867 selected = modelValue === valueFn(scope, locals); 26868 } 26869 selectedSet = selectedSet || selected; // see if at least one item is selected 26870 } 26871 label = displayFn(scope, locals); // what will be seen by the user 26872 26873 // doing displayFn(scope, locals) || '' overwrites zero values 26874 label = isDefined(label) ? label : ''; 26875 optionGroup.push({ 26876 // either the index into array or key from object 26877 id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index), 26878 label: label, 26879 selected: selected // determine if we should be selected 26880 }); 26881 } 26882 if (!multiple) { 26883 if (nullOption || modelValue === null) { 26884 // insert null option if we have a placeholder, or the model is null 26885 optionGroups[''].unshift({id:'', label:'', selected:!selectedSet}); 26886 } else if (!selectedSet) { 26887 // option could not be found, we have to insert the undefined item 26888 optionGroups[''].unshift({id:'?', label:'', selected:true}); 26889 } 26890 } 26891 26892 // Now we need to update the list of DOM nodes to match the optionGroups we computed above 26893 for (groupIndex = 0, groupLength = optionGroupNames.length; 26894 groupIndex < groupLength; 26895 groupIndex++) { 26896 // current option group name or '' if no group 26897 optionGroupName = optionGroupNames[groupIndex]; 26898 26899 // list of options for that group. (first item has the parent) 26900 optionGroup = optionGroups[optionGroupName]; 26901 26902 if (optionGroupsCache.length <= groupIndex) { 26903 // we need to grow the optionGroups 26904 existingParent = { 26905 element: optGroupTemplate.clone().attr('label', optionGroupName), 26906 label: optionGroup.label 26907 }; 26908 existingOptions = [existingParent]; 26909 optionGroupsCache.push(existingOptions); 26910 selectElement.append(existingParent.element); 26911 } else { 26912 existingOptions = optionGroupsCache[groupIndex]; 26913 existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element 26914 26915 // update the OPTGROUP label if not the same. 26916 if (existingParent.label != optionGroupName) { 26917 existingParent.element.attr('label', existingParent.label = optionGroupName); 26918 } 26919 } 26920 26921 lastElement = null; // start at the beginning 26922 for(index = 0, length = optionGroup.length; index < length; index++) { 26923 option = optionGroup[index]; 26924 if ((existingOption = existingOptions[index+1])) { 26925 // reuse elements 26926 lastElement = existingOption.element; 26927 if (existingOption.label !== option.label) { 26928 lastElement.text(existingOption.label = option.label); 26929 } 26930 if (existingOption.id !== option.id) { 26931 lastElement.val(existingOption.id = option.id); 26932 } 26933 // lastElement.prop('selected') provided by jQuery has side-effects 26934 if (lastElement[0].selected !== option.selected) {
26935 lastElement.prop('selected', (existingOption.selected = option.selected)); 26936 if (msie) { 26937 // See #7692 26938 // The selected item wouldn't visually update on IE without this. 26939 // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well 26940 lastElement.prop('selected', existingOption.selected); 26941 } 26942 } 26943 } else { 26944 // grow elements 26945 26946 // if it's a null option 26947 if (option.id === '' && nullOption) { 26948 // put back the pre-compiled element 26949 element = nullOption; 26950 } else { 26951 // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but 26952 // in this version of jQuery on some browser the .text() returns a string 26953 // rather then the element. 26954 (element = optionTemplate.clone()) 26955 .val(option.id) 26956 .prop('selected', option.selected) 26957 .attr('selected', option.selected) 26958 .text(option.label); 26959 } 26960 26961 existingOptions.push(existingOption = { 26962 element: element, 26963 label: option.label, 26964 id: option.id, 26965 selected: option.selected 26966 }); 26967 if (lastElement) { 26968 lastElement.after(element); 26969 } else { 26970 existingParent.element.append(element); 26971 } 26972 lastElement = element; 26973 } 26974 } 26975 // remove any excessive OPTIONs in a group 26976 index++; // increment since the existingOptions[0] is parent element not OPTION 26977 while(existingOptions.length > index) { 26978 existingOptions.pop().element.remove(); 26979 } 26980 } 26981 // remove any excessive OPTGROUPs from select 26982 while(optionGroupsCache.length > groupIndex) { 26983 optionGroupsCache.pop()[0].element.remove(); 26984 } 26985 } 26986 } 26987 } 26988 }; 26989}]; 26990 26991var optionDirective = ['$interpolate', function($interpolate) { 26992 var nullSelectCtrl = { 26993 addOption: noop, 26994 removeOption: noop 26995 }; 26996 26997 return { 26998 restrict: 'E', 26999 priority: 100, 27000 compile: function(element, attr) { 27001 if (isUndefined(attr.value)) { 27002 var interpolateFn = $interpolate(element.text(), true); 27003 if (!interpolateFn) { 27004 attr.$set('value', element.text()); 27005 } 27006 } 27007 27008 return function (scope, element, attr) { 27009 var selectCtrlName = '$selectController', 27010 parent = element.parent(), 27011 selectCtrl = parent.data(selectCtrlName) || 27012 parent.parent().data(selectCtrlName); // in case we are in optgroup 27013 27014 if (selectCtrl && selectCtrl.databound) { 27015 // For some reason Opera defaults to true and if not overridden this messes up the repeater. 27016 // We don't want the view to drive the initialization of the model anyway. 27017 element.prop('selected', false); 27018 } else { 27019 selectCtrl = nullSelectCtrl; 27020 } 27021 27022 if (interpolateFn) { 27023 scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) { 27024 attr.$set('value', newVal); 27025 if (newVal !== oldVal) selectCtrl.removeOption(oldVal); 27026 selectCtrl.addOption(newVal); 27027 }); 27028 } else { 27029 selectCtrl.addOption(attr.value); 27030 } 27031 27032 element.on('$destroy', function() { 27033 selectCtrl.removeOption(attr.value); 27034 }); 27035 }; 27036 } 27037 }; 27038}]; 27039 27040var styleDirective = valueFn({ 27041 restrict: 'E', 27042 terminal: true 27043}); 27044 27045 if (window.angular.bootstrap) { 27046 //AngularJS is already loaded, so we can return here... 27047 console.log('WARNING: Tried to load angular more than once.'); 27048 return; 27049 } 27050 27051 //try to bind to jquery now so that one can write angular.element().read() 27052 //but we will rebind on bootstrap again. 27053 bindJQuery(); 27054 27055 publishExternalAPI(angular); 27056 27057 jqLite(document).ready(function() { 27058 angularInit(document, bootstrap); 27059 }); 27060 27061})(window, document); 27062 27063!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}</style>'); 27064/** 27065 * @license AngularJS v1.2.25 27066 * (c) 2010-2014 Google, Inc. http://angularjs.org 27067 * License: MIT 27068 */ 27069(function(window, angular, undefined) {'use strict'; 27070 27071/* jshint maxlen: false */ 27072 27073/** 27074 * @ngdoc module 27075 * @name ngAnimate 27076 * @description 27077 * 27078 * # ngAnimate 27079 * 27080 * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives. 27081 * 27082 * 27083 * <div doc-module-components="ngAnimate"></div> 27084 * 27085 * # Usage 27086 * 27087 * To see animations in action, all that is required is to define the appropriate CSS classes 27088 * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are: 27089 * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation 27090 * by using the `$animate` service. 27091 * 27092 * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives: 27093 * 27094 * | Directive | Supported Animations | 27095 * |---------------------------------------------------------- |----------------------------------------------------| 27096 * | {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move | 27097 * | {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave | 27098 * | {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave | 27099 * | {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave | 27100 * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave | 27101 * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove | 27102 * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) | 27103 * | {@link ng.directive:form#usage_animations form}
27103 | add and remove (dirty, pristine, valid, invalid & all other validations) | 27104 * | {@link ng.directive:ngModel#usage_animations ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | 27105 * 27106 * You can find out more information about animations upon visiting each directive page. 27107 * 27108 * Below is an example of how to apply animations to a directive that supports animation hooks: 27109 * 27110 * ```html 27111 * <style type="text/css"> 27112 * .slide.ng-enter, .slide.ng-leave { 27113 * -webkit-transition:0.5s linear all; 27114 * transition:0.5s linear all; 27115 * } 27116 * 27117 * .slide.ng-enter { } /* starting animations for enter */ 27118 * .slide.ng-enter.ng-enter-active { } /* terminal animations for enter */ 27119 * .slide.ng-leave { } /* starting animations for leave */ 27120 * .slide.ng-leave.ng-leave-active { } /* terminal animations for leave */ 27121 * </style> 27122 * 27123 * <!-- 27124 * the animate service will automatically add .ng-enter and .ng-leave to the element 27125 * to trigger the CSS transition/animations 27126 * --> 27127 * <ANY class="slide" ng-include="..."></ANY> 27128 * ``` 27129 * 27130 * Keep in mind that, by default, if an animation is running, any child elements cannot be animated 27131 * until the parent element's animation has completed. This blocking feature can be overridden by 27132 * placing the `ng-animate-children` attribute on a parent container tag. 27133 * 27134 * ```html 27135 * <div class="slide-animation" ng-if="on" ng-animate-children> 27136 * <div class="fade-animation" ng-if="on"> 27137 * <div class="explode-animation" ng-if="on"> 27138 * ... 27139 * </div> 27140 * </div> 27141 * </div> 27142 * ``` 27143 * 27144 * When the `on` expression value changes and an animation is triggered then each of the elements within 27145 * will all animate without the block being applied to child elements. 27146 * 27147 * <h2>CSS-defined Animations</h2> 27148 * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes 27149 * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported 27150 * and can be used to play along with this naming structure. 27151 * 27152 * The following code below demonstrates how to perform animations using **CSS transitions** with Angular: 27153 * 27154 * ```html 27155 * <style type="text/css"> 27156 * /* 27157 * The animate class is apart of the element and the ng-enter class 27158 * is attached to the element once the enter animation event is triggered 27159 * */ 27160 * .reveal-animation.ng-enter { 27161 * -webkit-transition: 1s linear all; /* Safari/Chrome */ 27162 * transition: 1s linear all; /* All other modern browsers and IE10+ */ 27163 * 27164 * /* The animation preparation code */ 27165 * opacity: 0; 27166 * } 27167 * 27168 * /* 27169 * Keep in mind that you want to combine both CSS 27170 * classes together to avoid any CSS-specificity 27171 * conflicts 27172 * */ 27173 * .reveal-animation.ng-enter.ng-enter-active { 27174 * /* The animation code itself */ 27175 * opacity: 1; 27176 * } 27177 * </style> 27178 * 27179 * <div class="view-container"> 27180 * <div ng-view class="reveal-animation"></div> 27181 * </div> 27182 * ``` 27183 * 27184 * The following code below demonstrates how to perform animations using **CSS animations** with Angular: 27185 * 27186 * ```html 27187 * <style type="text/css"> 27188 * .reveal-animation.ng-enter { 27189 * -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */ 27190 * animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */ 27191 * } 27192 * @-webkit-keyframes enter_sequence { 27193 * from { opacity:0; } 27194 * to { opacity:1; } 27195 * } 27196 * @keyframes enter_sequence { 27197 * from { opacity:0; } 27198 * to { opacity:1; } 27199 * } 27200 * </style> 27201 * 27202 * <div class="view-container"> 27203 * <div ng-view class="reveal-animation"></div> 27204 * </div> 27205 * ``` 27206 * 27207 * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing. 27208 * 27209 * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add 27210 * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically 27211 * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be 27212 * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end 27213 * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element 27214 * has no CSS transition/animation classes applied to it. 27215 * 27216 * <h3>CSS Staggering Animations</h3> 27217 * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a 27218 * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be 27219 * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for 27220 * the animation. The style property expected within the stagger class can either be a **transition-delay** or an 27221 * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). 27222 * 27223 * ```css 27224 * .my-animation.ng-enter { 27225 * /* standard transition code */ 27226 * -webkit-transition: 1s linear all; 27227 * transition: 1s linear all; 27228 * opacity:0; 27229 * } 27230 * .my-animation.ng-enter-stagger { 27231 * /* this will have a 100ms delay between each successive leave animation */ 27232 * -webkit-transition-delay: 0.1s; 27233 * transition-delay: 0.1s; 27234 * 27235 * /* in case the stagger doesn't work then these two values 27236 * must be set to 0 to avoid an accidental CSS inheritance */ 27237 * -webkit-transition-duration: 0s; 27238 * transition-duration: 0s; 27239 * } 27240 * .my-animation.ng-enter.ng-enter-active { 27241 * /* standard transition styles */ 27242 * opacity:1; 27243 * } 27244 * ``` 27245 * 27246 * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations 27247 * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this 27248 * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation 27249 * will also be reset if more than 10ms has passed after the last animation has been fired. 27250 * 27251 * The following code will issue the **ng-leave-stagger** event on the element provided: 27252 * 27253 * ```js 27254 * var kids = parent.children();
27255 * 27256 * $animate.leave(kids[0]); //stagger index=0 27257 * $animate.leave(kids[1]); //stagger index=1 27258 * $animate.leave(kids[2]); //stagger index=2 27259 * $animate.leave(kids[3]); //stagger index=3 27260 * $animate.leave(kids[4]); //stagger index=4 27261 * 27262 * $timeout(function() { 27263 * //stagger has reset itself 27264 * $animate.leave(kids[5]); //stagger index=0 27265 * $animate.leave(kids[6]); //stagger index=1 27266 * }, 100, false); 27267 * ``` 27268 * 27269 * Stagger animations are currently only supported within CSS-defined animations. 27270 * 27271 * <h2>JavaScript-defined Animations</h2> 27272 * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not 27273 * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module. 27274 * 27275 * ```js 27276 * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application. 27277 * var ngModule = angular.module('YourApp', ['ngAnimate']); 27278 * ngModule.animation('.my-crazy-animation', function() { 27279 * return { 27280 * enter: function(element, done) { 27281 * //run the animation here and call done when the animation is complete 27282 * return function(cancelled) { 27283 * //this (optional) function will be called when the animation 27284 * //completes or when the animation is cancelled (the cancelled 27285 * //flag will be set to true if cancelled). 27286 * }; 27287 * }, 27288 * leave: function(element, done) { }, 27289 * move: function(element, done) { }, 27290 * 27291 * //animation that can be triggered before the class is added 27292 * beforeAddClass: function(element, className, done) { }, 27293 * 27294 * //animation that can be triggered after the class is added 27295 * addClass: function(element, className, done) { }, 27296 * 27297 * //animation that can be triggered before the class is removed 27298 * beforeRemoveClass: function(element, className, done) { }, 27299 * 27300 * //animation that can be triggered after the class is removed 27301 * removeClass: function(element, className, done) { } 27302 * }; 27303 * }); 27304 * ``` 27305 * 27306 * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run 27307 * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits 27308 * the element's CSS class attribute value and then run the matching animation event function (if found). 27309 * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will 27310 * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported). 27311 * 27312 * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned. 27313 * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run, 27314 * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation 27315 * or transition code that is defined via a stylesheet). 27316 * 27317 */ 27318 27319angular.module('ngAnimate', ['ng']) 27320 27321 /** 27322 * @ngdoc provider 27323 * @name $animateProvider 27324 * @description 27325 * 27326 * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module. 27327 * When an animation is triggered, the $animate service will query the $animate service to find any animations that match 27328 * the provided name value. 27329 * 27330 * Requires the {@link ngAnimate `ngAnimate`} module to be installed. 27331 * 27332 * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. 27333 * 27334 */ 27335 .directive('ngAnimateChildren', function() { 27336 var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; 27337 return function(scope, element, attrs) { 27338 var val = attrs.ngAnimateChildren; 27339 if(angular.isString(val) && val.length === 0) { //empty attribute 27340 element.data(NG_ANIMATE_CHILDREN, true); 27341 } else { 27342 scope.$watch(val, function(value) { 27343 element.data(NG_ANIMATE_CHILDREN, !!value); 27344 }); 27345 } 27346 }; 27347 }) 27348 27349 //this private service is only used within CSS-enabled animations 27350 //IE8 + IE9 do not support rAF natively, but that is fine since they 27351 //also don't support transitions and keyframes which means that the code 27352 //below will never be used by the two browsers. 27353 .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) { 27354 var bod = $document[0].body; 27355 return function(fn) { 27356 //the returned function acts as the cancellation function 27357 return $$rAF(function() { 27358 //the line below will force the browser to perform a repaint 27359 //so that all the animated elements within the animation frame 27360 //will be properly updated and drawn on screen. This is 27361 //required to perform multi-class CSS based animations with 27362 //Firefox. DO NOT REMOVE THIS LINE. 27363 var a = bod.offsetWidth + 1; 27364 fn(); 27365 }); 27366 }; 27367 }]) 27368 27369 .config(['$provide', '$animateProvider', function($provide, $animateProvider) { 27370 var noop = angular.noop;
27371 var forEach = angular.forEach; 27372 var selectors = $animateProvider.$$selectors; 27373 27374 var ELEMENT_NODE = 1; 27375 var NG_ANIMATE_STATE = '$$ngAnimateState'; 27376 var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; 27377 var NG_ANIMATE_CLASS_NAME = 'ng-animate'; 27378 var rootAnimateState = {running: true}; 27379 27380 function extractElementNode(element) { 27381 for(var i = 0; i < element.length; i++) { 27382 var elm = element[i]; 27383 if(elm.nodeType == ELEMENT_NODE) { 27384 return elm; 27385 } 27386 } 27387 } 27388 27389 function prepareElement(element) { 27390 return element && angular.element(element); 27391 } 27392 27393 function stripCommentsFromElement(element) { 27394 return angular.element(extractElementNode(element)); 27395 } 27396 27397 function isMatchingElement(elm1, elm2) { 27398 return extractElementNode(elm1) == extractElementNode(elm2); 27399 } 27400 27401 $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', 27402 function($delegate, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document) { 27403 27404 var globalAnimationCounter = 0; 27405 $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); 27406 27407 // disable animations during bootstrap, but once we bootstrapped, wait again 27408 // for another digest until enabling animations. The reason why we digest twice 27409 // is because all structural animations (enter, leave and move) all perform a 27410 // post digest operation before animating. If we only wait for a single digest 27411 // to pass then the structural animation would render its animation on page load. 27412 // (which is what we're trying to avoid when the application first boots up.) 27413 $rootScope.$$postDigest(function() { 27414 $rootScope.$$postDigest(function() { 27415 rootAnimateState.running = false; 27416 }); 27417 }); 27418 27419 var classNameFilter = $animateProvider.classNameFilter(); 27420 var isAnimatableClassName = !classNameFilter 27421 ? function() { return true; } 27422 : function(className) { 27423 return classNameFilter.test(className); 27424 }; 27425 27426 function blockElementAnimations(element) { 27427 var data = element.data(NG_ANIMATE_STATE) || {}; 27428 data.running = true; 27429 element.data(NG_ANIMATE_STATE, data); 27430 } 27431 27432 function lookup(name) { 27433 if (name) { 27434 var matches = [], 27435 flagMap = {}, 27436 classes = name.substr(1).split('.'); 27437 27438 //the empty string value is the default animation 27439 //operation which performs CSS transition and keyframe 27440 //animations sniffing. This is always included for each 27441 //element animation procedure if the browser supports 27442 //transitions and/or keyframe animations. The default 27443 //animation is added to the top of the list to prevent 27444 //any previous animations from affecting the element styling 27445 //prior to the element being animated. 27446 if ($sniffer.transitions || $sniffer.animations) { 27447 matches.push($injector.get(selectors[''])); 27448 } 27449 27450 for(var i=0; i < classes.length; i++) { 27451 var klass = classes[i], 27452 selectorFactoryName = selectors[klass]; 27453 if(selectorFactoryName && !flagMap[klass]) { 27454 matches.push($injector.get(selectorFactoryName)); 27455 flagMap[klass] = true; 27456 } 27457 } 27458 return matches; 27459 } 27460 } 27461 27462 function animationRunner(element, animationEvent, className) { 27463 //transcluded directives may sometimes fire an animation using only comment nodes 27464 //best to catch this early on to prevent any animation operations from occurring 27465 var node = element[0]; 27466 if(!node) { 27467 return; 27468 } 27469 27470 var isSetClassOperation = animationEvent == 'setClass'; 27471 var isClassBased = isSetClassOperation || 27472 animationEvent == 'addClass' || 27473 animationEvent == 'removeClass'; 27474 27475 var classNameAdd, classNameRemove; 27476 if(angular.isArray(className)) { 27477 classNameAdd = className[0];
27478 classNameRemove = className[1]; 27479 className = classNameAdd + ' ' + classNameRemove; 27480 } 27481 27482 var currentClassName = element.attr('class'); 27483 var classes = currentClassName + ' ' + className; 27484 if(!isAnimatableClassName(classes)) { 27485 return; 27486 } 27487 27488 var beforeComplete = noop, 27489 beforeCancel = [], 27490 before = [], 27491 afterComplete = noop, 27492 afterCancel = [], 27493 after = []; 27494 27495 var animationLookup = (' ' + classes).replace(/\s+/g,'.'); 27496 forEach(lookup(animationLookup), function(animationFactory) { 27497 var created = registerAnimation(animationFactory, animationEvent); 27498 if(!created && isSetClassOperation) { 27499 registerAnimation(animationFactory, 'addClass'); 27500 registerAnimation(animationFactory, 'removeClass'); 27501 } 27502 }); 27503 27504 function registerAnimation(animationFactory, event) { 27505 var afterFn = animationFactory[event]; 27506 var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)]; 27507 if(afterFn || beforeFn) { 27508 if(event == 'leave') { 27509 beforeFn = afterFn; 27510 //when set as null then animation knows to skip this phase 27511 afterFn = null; 27512 } 27513 after.push({ 27514 event : event, fn : afterFn 27515 }); 27516 before.push({ 27517 event : event, fn : beforeFn 27518 }); 27519 return true; 27520 } 27521 } 27522 27523 function run(fns, cancellations, allCompleteFn) { 27524 var animations = []; 27525 forEach(fns, function(animation) { 27526 animation.fn && animations.push(animation); 27527 }); 27528 27529 var count = 0; 27530 function afterAnimationComplete(index) { 27531 if(cancellations) { 27532 (cancellations[index] || noop)(); 27533 if(++count < animations.length) return; 27534 cancellations = null; 27535 } 27536 allCompleteFn(); 27537 } 27538 27539 //The code below adds directly to the array in order to work with 27540 //both sync and async animations. Sync animations are when the done() 27541 //operation is called right away. DO NOT REFACTOR! 27542 forEach(animations, function(animation, index) { 27543 var progress = function() { 27544 afterAnimationComplete(index); 27545 }; 27546 switch(animation.event) { 27547 case 'setClass': 27548 cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress)); 27549 break; 27550 case 'addClass': 27551 cancellations.push(animation.fn(element, classNameAdd || className, progress)); 27552 break; 27553 case 'removeClass': 27554 cancellations.push(animation.fn(element, classNameRemove || className, progress)); 27555 break; 27556 default: 27557 cancellations.push(animation.fn(element, progress)); 27558 break; 27559 } 27560 }); 27561 27562 if(cancellations && cancellations.length === 0) { 27563 allCompleteFn(); 27564 } 27565 } 27566 27567 return { 27568 node : node, 27569 event : animationEvent, 27570 className : className, 27571 isClassBased : isClassBased, 27572 isSetClassOperation : isSetClassOperation, 27573 before : function(allCompleteFn) { 27574 beforeComplete = allCompleteFn; 27575 run(before, beforeCancel, function() { 27576 beforeComplete = noop; 27577 allCompleteFn(); 27578 }); 27579 }, 27580 after : function(allCompleteFn) { 27581 afterComplete = allCompleteFn; 27582 run(after, afterCancel, function() { 27583 afterComplete = noop; 27584 allCompleteFn(); 27585 }); 27586 }, 27587 cancel : function() { 27588 if(beforeCancel) { 27589 forEach(beforeCancel, function(cancelFn) { 27590 (cancelFn || noop)(true); 27591 }); 27592 beforeComplete(true); 27593 } 27594 if(afterCancel) {
27595 forEach(afterCancel, function(cancelFn) { 27596 (cancelFn || noop)(true); 27597 }); 27598 afterComplete(true); 27599 } 27600 } 27601 }; 27602 } 27603 27604 /** 27605 * @ngdoc service 27606 * @name $animate 27607 * @kind function 27608 * 27609 * @description 27610 * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations. 27611 * When any of these operations are run, the $animate service 27612 * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object) 27613 * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run. 27614 * 27615 * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives 27616 * will work out of the box without any extra configuration. 27617 * 27618 * Requires the {@link ngAnimate `ngAnimate`} module to be installed. 27619 * 27620 * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. 27621 * 27622 */ 27623 return { 27624 /** 27625 * @ngdoc method 27626 * @name $animate#enter 27627 * @kind function 27628 * 27629 * @description 27630 * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once 27631 * the animation is started, the following CSS classes will be present on the element for the duration of the animation: 27632 * 27633 * Below is a breakdown of each step that occurs during enter animation: 27634 * 27635 * | Animation Step | What the element class attribute looks like | 27636 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 27637 * | 1. $animate.enter(...) is called | class="my-animation" | 27638 * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" | 27639 * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 27640 * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" | 27641 * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" | 27642 * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" | 27643 * | 7. the .ng-enter-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" | 27644 * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" | 27645 * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 27646 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" | 27647 * 27648 * @param {DOMElement} element the element that will be the focus of the enter animation 27649 * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation 27650 * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation 27651 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 27652 */ 27653 enter : function(element, parentElement, afterElement, doneCallback) { 27654 element = angular.element(element); 27655 parentElement = prepareElement(parentElement); 27656 afterElement = prepareElement(afterElement); 27657 27658 blockElementAnimations(element); 27659 $delegate.enter(element, parentElement, afterElement); 27660 $rootScope.$$postDigest(function() { 27661 element = stripCommentsFromElement(element); 27662 performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback); 27663 }); 27664 }, 27665 27666 /** 27667 * @ngdoc method 27668 * @name $animate#leave 27669 * @kind function 27670 *
27671 * @description 27672 * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once 27673 * the animation is started, the following CSS classes will be added for the duration of the animation: 27674 * 27675 * Below is a breakdown of each step that occurs during leave animation: 27676 * 27677 * | Animation Step | What the element class attribute looks like | 27678 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 27679 * | 1. $animate.leave(...) is called | class="my-animation" | 27680 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 27681 * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" | 27682 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" | 27683 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" | 27684 * | 6. the .ng-leave-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" | 27685 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" | 27686 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 27687 * | 9. The element is removed from the DOM | ... | 27688 * | 10. The doneCallback() callback is fired (if provided) | ... | 27689 * 27690 * @param {DOMElement} element the element that will be the focus of the leave animation 27691 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 27692 */ 27693 leave : function(element, doneCallback) { 27694 element = angular.element(element); 27695 cancelChildAnimations(element); 27696 blockElementAnimations(element); 27697 $rootScope.$$postDigest(function() { 27698 performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() { 27699 $delegate.leave(element); 27700 }, doneCallback); 27701 }); 27702 }, 27703 27704 /** 27705 * @ngdoc method 27706 * @name $animate#move 27707 * @kind function 27708 * 27709 * @description 27710 * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or 27711 * add the element directly after the afterElement element if present. Then the move animation will be run. Once 27712 * the animation is started, the following CSS classes will be added for the duration of the animation: 27713 * 27714 * Below is a breakdown of each step that occurs during move animation: 27715 * 27716 * | Animation Step | What the element class attribute looks like | 27717 * |----------------------------------------------------------------------------------------------|---------------------------------------------| 27718 * | 1. $animate.move(...) is called | class="my-animation" | 27719 * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" | 27720 * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 27721 * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" | 27722 * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" | 27723 * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" | 27724 * | 7. the .ng-move-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" | 27725 * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" | 27726 * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 27727 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" | 27728 * 27729 * @param {DOMElement} element the element that will be the focus of the move animation 27730 * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation 27731 * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation 27732 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 27733 */ 27734 move : function(element, parentElement, afterElement, doneCallback) { 27735 element = angular.element(element); 27736 parentElement = prepareElement(parentElement); 27737 afterElement = prepareElement(afterElement); 27738 27739 cancelChildAnimations(element); 27740 blockElementAnimations(element); 27741 $delegate.move(element, parentElement, afterElement); 27742 $rootScope.$$postDigest(function() { 27743 element = stripCommentsFromElement(element); 27744 performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback); 27745 }); 27746 }, 27747 27748 /** 27749 * @ngdoc method 27750 * @name $animate#addClass 27751 *
27752 * @description 27753 * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class. 27754 * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide 27755 * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions 27756 * or keyframes are defined on the -add or base CSS class). 27757 * 27758 * Below is a breakdown of each step that occurs during addClass animation: 27759 * 27760 * | Animation Step | What the element class attribute looks like | 27761 * |------------------------------------------------------------------------------------------------|---------------------------------------------| 27762 * | 1. $animate.addClass(element, 'super') is called | class="my-animation" | 27763 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" | 27764 * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" | 27765 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" | 27766 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" | 27767 * | 6. the .super, .super-add-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super super-add super-add-active" | 27768 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super super-add super-add-active" | 27769 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" | 27770 * | 9. The super class is kept on the element | class="my-animation super" | 27771 * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" | 27772 * 27773 * @param {DOMElement} element the element that will be animated 27774 * @param {string} className the CSS class that will be added to the element and then animated 27775 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 27776 */ 27777 addClass : function(element, className, doneCallback) { 27778 element = angular.element(element); 27779 element = stripCommentsFromElement(element); 27780 performAnimation('addClass', className, element, null, null, function() { 27781 $delegate.addClass(element, className); 27782 }, doneCallback); 27783 }, 27784 27785 /** 27786 * @ngdoc method 27787 * @name $animate#removeClass 27788 * 27789 * @description 27790 * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value 27791 * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in 27792 * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if 27793 * no CSS transitions or keyframes are defined on the -remove or base CSS classes). 27794 * 27795 * Below is a breakdown of each step that occurs during removeClass animation: 27796 * 27797 * | Animation Step | What the element class attribute looks like | 27798 * |-----------------------------------------------------------------------------------------------|---------------------------------------------| 27799 * | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" | 27800 * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" | 27801 * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"| 27802 * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" | 27803 * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" | 27804 * | 6. the .super-remove-active and .ng-animate-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" | 27805 * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" | 27806 * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | 27807 * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" | 27808 * 27809 * 27810 * @param {DOMElement} element the element that will be animated 27811 * @param {string} className the CSS class that will be animated and then removed from the element 27812 * @param {function()=} doneCallback the callback function that will be called once the animation is complete 27813 */ 27814 removeClass : function(element, className, doneCallback) { 27815 element = angular.element(element); 27816 element = stripCommentsFromElement(element); 27817 performAnimation('removeClass', className, element, null, null, function() { 27818 $delegate.removeClass(element, className); 27819 }, doneCallback); 27820 }, 27821 27822 /** 27823 * 27824 * @ngdoc function 27825 * @name $animate#setClass 27826 * @function
27827 * @description Adds and/or removes the given CSS classes to and from the element. 27828 * Once complete, the done() callback will be fired (if provided). 27829 * @param {DOMElement} element the element which will its CSS classes changed 27830 * removed from it 27831 * @param {string} add the CSS classes which will be added to the element 27832 * @param {string} remove the CSS class which will be removed from the element 27833 * @param {Function=} done the callback function (if provided) that will be fired after the 27834 * CSS classes have been set on the element 27835 */ 27836 setClass : function(element, add, remove, doneCallback) { 27837 element = angular.element(element); 27838 element = stripCommentsFromElement(element); 27839 performAnimation('setClass', [add, remove], element, null, null, function() { 27840 $delegate.setClass(element, add, remove); 27841 }, doneCallback); 27842 }, 27843 27844 /** 27845 * @ngdoc method 27846 * @name $animate#enabled 27847 * @kind function 27848 * 27849 * @param {boolean=} value If provided then set the animation on or off. 27850 * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation 27851 * @return {boolean} Current animation state. 27852 * 27853 * @description 27854 * Globally enables/disables animations. 27855 * 27856 */ 27857 enabled : function(value, element) { 27858 switch(arguments.length) { 27859 case 2: 27860 if(value) { 27861 cleanup(element); 27862 } else { 27863 var data = element.data(NG_ANIMATE_STATE) || {}; 27864 data.disabled = true; 27865 element.data(NG_ANIMATE_STATE, data); 27866 } 27867 break; 27868 27869 case 1: 27870 rootAnimateState.disabled = !value; 27871 break; 27872 27873 default: 27874 value = !rootAnimateState.disabled; 27875 break; 27876 } 27877 return !!value; 27878 } 27879 }; 27880 27881 /* 27882 all animations call this shared animation triggering function internally. 27883 The animationEvent variable refers to the JavaScript animation event that will be triggered 27884 and the className value is the name of the animation that will be applied within the 27885 CSS code. Element, parentElement and afterElement are provided DOM elements for the animation 27886 and the onComplete callback will be fired once the animation is fully complete. 27887 */ 27888 function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) { 27889 27890 var runner = animationRunner(element, animationEvent, className); 27891 if(!runner) { 27892 fireDOMOperation(); 27893 fireBeforeCallbackAsync(); 27894 fireAfterCallbackAsync(); 27895 closeAnimation(); 27896 return; 27897 } 27898 27899 className = runner.className; 27900 var elementEvents = angular.element._data(runner.node); 27901 elementEvents = elementEvents && elementEvents.events; 27902 27903 if (!parentElement) { 27904 parentElement = afterElement ? afterElement.parent() : element.parent(); 27905 } 27906 27907 var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; 27908 var runningAnimations = ngAnimateState.active || {}; 27909 var totalActiveAnimations = ngAnimateState.totalActive || 0; 27910 var lastAnimation = ngAnimateState.last; 27911 27912 //only allow animations if the currently running animation is not structural 27913 //or if there is no animation running at all 27914 var skipAnimations; 27915 if (runner.isClassBased) { 27916 skipAnimations = ngAnimateState.running || 27917 ngAnimateState.disabled || 27918 (lastAnimation && !lastAnimation.isClassBased); 27919 } 27920 27921 //skip the animation if animations are disabled, a parent is already being animated, 27922 //the element is not currently attached to the document body or then completely close 27923 //the animation if any matching animations are not found at all. 27924 //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found. 27925 if (skipAnimations || animationsDisabled(element, parentElement)) { 27926 fireDOMOperation(); 27927 fireBeforeCallbackAsync(); 27928 fireAfterCallbackAsync(); 27929 closeAnimation(); 27930 return; 27931 } 27932 27933 var skipAnimation = false; 27934 if(totalActiveAnimations > 0) { 27935 var animationsToCancel = []; 27936 if(!runner.isClassBased) { 27937 if(animationEvent == 'leave' && runningAnimations['ng-leave']) { 27938 skipAnimation = true; 27939 } else { 27940 //cancel all animations when a structural animation takes place 27941 for(var klass in runningAnimations) { 27942 animationsToCancel.push(runningAnimations[klass]); 27943 cleanup(element, klass); 27944 } 27945 runningAnimations = {}; 27946 totalActiveAnimations = 0; 27947 } 27948 } else if(lastAnimation.event == 'setClass') { 27949 animationsToCancel.push(lastAnimation); 27950 cleanup(element, className); 27951 } 27952 else if(runningAnimations[className]) { 27953 var current = runningAnimations[className]; 27954 if(current.event == animationEvent) { 27955 skipAnimation = true; 27956 } else { 27957 animationsToCancel.push(current); 27958 cleanup(element, className); 27959 } 27960 } 27961 27962 if(animationsToCancel.length > 0) {
27963 forEach(animationsToCancel, function(operation) { 27964 operation.cancel(); 27965 }); 27966 } 27967 } 27968 27969 if(runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) { 27970 skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR 27971 } 27972 27973 if(skipAnimation) { 27974 fireDOMOperation(); 27975 fireBeforeCallbackAsync(); 27976 fireAfterCallbackAsync(); 27977 fireDoneCallbackAsync(); 27978 return; 27979 } 27980 27981 if(animationEvent == 'leave') { 27982 //there's no need to ever remove the listener since the element 27983 //will be removed (destroyed) after the leave animation ends or 27984 //is cancelled midway 27985 element.one('$destroy', function(e) { 27986 var element = angular.element(this); 27987 var state = element.data(NG_ANIMATE_STATE); 27988 if(state) { 27989 var activeLeaveAnimation = state.active['ng-leave']; 27990 if(activeLeaveAnimation) { 27991 activeLeaveAnimation.cancel(); 27992 cleanup(element, 'ng-leave'); 27993 } 27994 } 27995 }); 27996 } 27997 27998 //the ng-animate class does nothing, but it's here to allow for 27999 //parent animations to find and cancel child animations when needed 28000 element.addClass(NG_ANIMATE_CLASS_NAME); 28001 28002 var localAnimationCount = globalAnimationCounter++; 28003 totalActiveAnimations++; 28004 runningAnimations[className] = runner; 28005 28006 element.data(NG_ANIMATE_STATE, { 28007 last : runner, 28008 active : runningAnimations, 28009 index : localAnimationCount, 28010 totalActive : totalActiveAnimations 28011 }); 28012 28013 //first we run the before animations and when all of those are complete 28014 //then we perform the DOM operation and run the next set of animations 28015 fireBeforeCallbackAsync(); 28016 runner.before(function(cancelled) { 28017 var data = element.data(NG_ANIMATE_STATE); 28018 cancelled = cancelled || 28019 !data || !data.active[className] || 28020 (runner.isClassBased && data.active[className].event != animationEvent); 28021 28022 fireDOMOperation(); 28023 if(cancelled === true) { 28024 closeAnimation(); 28025 } else { 28026 fireAfterCallbackAsync(); 28027 runner.after(closeAnimation); 28028 } 28029 }); 28030 28031 function fireDOMCallback(animationPhase) { 28032 var eventName = '$animate:' + animationPhase; 28033 if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) { 28034 $$asyncCallback(function() { 28035 element.triggerHandler(eventName, { 28036 event : animationEvent, 28037 className : className 28038 }); 28039 }); 28040 } 28041 } 28042 28043 function fireBeforeCallbackAsync() { 28044 fireDOMCallback('before'); 28045 } 28046 28047 function fireAfterCallbackAsync() { 28048 fireDOMCallback('after'); 28049 } 28050 28051 function fireDoneCallbackAsync() { 28052 fireDOMCallback('close'); 28053 if(doneCallback) { 28054 $$asyncCallback(function() { 28055 doneCallback(); 28056 }); 28057 } 28058 } 28059 28060 //it is less complicated to use a flag than managing and canceling 28061 //timeouts containing multiple callbacks. 28062 function fireDOMOperation() { 28063 if(!fireDOMOperation.hasBeenRun) { 28064 fireDOMOperation.hasBeenRun = true; 28065 domOperation(); 28066 } 28067 } 28068 28069 function closeAnimation() { 28070 if(!closeAnimation.hasBeenRun) { 28071 closeAnimation.hasBeenRun = true; 28072 var data = element.data(NG_ANIMATE_STATE); 28073 if(data) { 28074 /* only structural animations wait for reflow before removing an 28075 animation, but class-based animations don't. An example of this 28076 failing would be when a parent HTML tag has a ng-class attribute 28077 causing ALL directives below to skip animations during the digest */ 28078 if(runner && runner.isClassBased) { 28079 cleanup(element, className); 28080 } else { 28081 $$asyncCallback(function() { 28082 var data = element.data(NG_ANIMATE_STATE) || {}; 28083 if(localAnimationCount == data.index) { 28084 cleanup(element, className, animationEvent); 28085 } 28086 }); 28087 element.data(NG_ANIMATE_STATE, data); 28088 } 28089 } 28090 fireDoneCallbackAsync(); 28091 } 28092 } 28093 } 28094 28095 function cancelChildAnimations(element) { 28096 var node = extractElementNode(element); 28097 if (node) { 28098 var nodes = angular.isFunction(node.getElementsByClassName) ? 28099 node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) : 28100 node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME);
28101 forEach(nodes, function(element) { 28102 element = angular.element(element); 28103 var data = element.data(NG_ANIMATE_STATE); 28104 if(data && data.active) { 28105 forEach(data.active, function(runner) { 28106 runner.cancel(); 28107 }); 28108 } 28109 }); 28110 } 28111 } 28112 28113 function cleanup(element, className) { 28114 if(isMatchingElement(element, $rootElement)) { 28115 if(!rootAnimateState.disabled) { 28116 rootAnimateState.running = false; 28117 rootAnimateState.structural = false; 28118 } 28119 } else if(className) { 28120 var data = element.data(NG_ANIMATE_STATE) || {}; 28121 28122 var removeAnimations = className === true; 28123 if(!removeAnimations && data.active && data.active[className]) { 28124 data.totalActive--; 28125 delete data.active[className]; 28126 } 28127 28128 if(removeAnimations || !data.totalActive) { 28129 element.removeClass(NG_ANIMATE_CLASS_NAME); 28130 element.removeData(NG_ANIMATE_STATE); 28131 } 28132 } 28133 } 28134 28135 function animationsDisabled(element, parentElement) { 28136 if (rootAnimateState.disabled) { 28137 return true; 28138 } 28139 28140 if (isMatchingElement(element, $rootElement)) { 28141 return rootAnimateState.running; 28142 } 28143 28144 var allowChildAnimations, parentRunningAnimation, hasParent; 28145 do { 28146 //the element did not reach the root element which means that it 28147 //is not apart of the DOM. Therefore there is no reason to do 28148 //any animations on it 28149 if (parentElement.length === 0) break; 28150 28151 var isRoot = isMatchingElement(parentElement, $rootElement); 28152 var state = isRoot ? rootAnimateState : (parentElement.data(NG_ANIMATE_STATE) || {}); 28153 if (state.disabled) { 28154 return true; 28155 } 28156 28157 //no matter what, for an animation to work it must reach the root element 28158 //this implies that the element is attached to the DOM when the animation is run 28159 if (isRoot) { 28160 hasParent = true; 28161 } 28162 28163 //once a flag is found that is strictly false then everything before 28164 //it will be discarded and all child animations will be restricted 28165 if (allowChildAnimations !== false) { 28166 var animateChildrenFlag = parentElement.data(NG_ANIMATE_CHILDREN); 28167 if(angular.isDefined(animateChildrenFlag)) { 28168 allowChildAnimations = animateChildrenFlag; 28169 } 28170 } 28171 28172 parentRunningAnimation = parentRunningAnimation || 28173 state.running || 28174 (state.last && !state.last.isClassBased); 28175 } 28176 while(parentElement = parentElement.parent()); 28177 28178 return !hasParent || (!allowChildAnimations && parentRunningAnimation); 28179 } 28180 }]); 28181 28182 $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow', 28183 function($window, $sniffer, $timeout, $$animateReflow) { 28184 // Detect proper transitionend/animationend event names. 28185 var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; 28186 28187 // If unprefixed events are not supported but webkit-prefixed are, use the latter.
28188 // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them. 28189 // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend` 28190 // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`. 28191 // Register both events in case `window.onanimationend` is not supported because of that, 28192 // do the same for `transitionend` as Safari is likely to exhibit similar behavior. 28193 // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit 28194 // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition 28195 if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { 28196 CSS_PREFIX = '-webkit-'; 28197 TRANSITION_PROP = 'WebkitTransition'; 28198 TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; 28199 } else { 28200 TRANSITION_PROP = 'transition'; 28201 TRANSITIONEND_EVENT = 'transitionend'; 28202 } 28203 28204 if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) { 28205 CSS_PREFIX = '-webkit-'; 28206 ANIMATION_PROP = 'WebkitAnimation'; 28207 ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; 28208 } else { 28209 ANIMATION_PROP = 'animation'; 28210 ANIMATIONEND_EVENT = 'animationend'; 28211 } 28212 28213 var DURATION_KEY = 'Duration'; 28214 var PROPERTY_KEY = 'Property'; 28215 var DELAY_KEY = 'Delay'; 28216 var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; 28217 var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey'; 28218 var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data'; 28219 var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions'; 28220 var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; 28221 var CLOSING_TIME_BUFFER = 1.5; 28222 var ONE_SECOND = 1000; 28223 28224 var lookupCache = {}; 28225 var parentCounter = 0; 28226 var animationReflowQueue = []; 28227 var cancelAnimationReflow; 28228 function afterReflow(element, callback) { 28229 if(cancelAnimationReflow) { 28230 cancelAnimationReflow(); 28231 } 28232 animationReflowQueue.push(callback); 28233 cancelAnimationReflow = $$animateReflow(function() { 28234 forEach(animationReflowQueue, function(fn) { 28235 fn(); 28236 }); 28237 28238 animationReflowQueue = []; 28239 cancelAnimationReflow = null; 28240 lookupCache = {}; 28241 }); 28242 } 28243 28244 var closingTimer = null; 28245 var closingTimestamp = 0; 28246 var animationElementQueue = []; 28247 function animationCloseHandler(element, totalTime) { 28248 var node = extractElementNode(element); 28249 element = angular.element(node); 28250 28251 //this item will be garbage collected by the closing 28252 //animation timeout 28253 animationElementQueue.push(element); 28254 28255 //but it may not need to cancel out the existing timeout 28256 //if the timestamp is less than the previous one 28257 var futureTimestamp = Date.now() + totalTime; 28258 if(futureTimestamp <= closingTimestamp) { 28259 return; 28260 } 28261 28262 $timeout.cancel(closingTimer); 28263 28264 closingTimestamp = futureTimestamp; 28265 closingTimer = $timeout(function() { 28266 closeAllAnimations(animationElementQueue); 28267 animationElementQueue = []; 28268 }, totalTime, false); 28269 } 28270 28271 function closeAllAnimations(elements) { 28272 forEach(elements, function(element) { 28273 var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); 28274 if(elementData) { 28275 (elementData.closeAnimationFn || noop)(); 28276 } 28277 }); 28278 } 28279 28280 function getElementAnimationDetails(element, cacheKey) { 28281 var data = cacheKey ? lookupCache[cacheKey] : null; 28282 if(!data) { 28283 var transitionDuration = 0; 28284 var transitionDelay = 0; 28285 var animationDuration = 0; 28286 var animationDelay = 0; 28287 var transitionDelayStyle; 28288 var animationDelayStyle; 28289 var transitionDurationStyle; 28290 var transitionPropertyStyle; 28291 28292 //we want all the styles defined before and after
28293 forEach(element, function(element) { 28294 if (element.nodeType == ELEMENT_NODE) { 28295 var elementStyles = $window.getComputedStyle(element) || {}; 28296 28297 transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY]; 28298 28299 transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration); 28300 28301 transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY]; 28302 28303 transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY]; 28304 28305 transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay); 28306 28307 animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY]; 28308 28309 animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay); 28310 28311 var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]); 28312 28313 if(aDuration > 0) { 28314 aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1; 28315 } 28316 28317 animationDuration = Math.max(aDuration, animationDuration); 28318 } 28319 }); 28320 data = { 28321 total : 0, 28322 transitionPropertyStyle: transitionPropertyStyle, 28323 transitionDurationStyle: transitionDurationStyle, 28324 transitionDelayStyle: transitionDelayStyle, 28325 transitionDelay: transitionDelay, 28326 transitionDuration: transitionDuration, 28327 animationDelayStyle: animationDelayStyle, 28328 animationDelay: animationDelay, 28329 animationDuration: animationDuration 28330 }; 28331 if(cacheKey) { 28332 lookupCache[cacheKey] = data; 28333 } 28334 } 28335 return data; 28336 } 28337 28338 function parseMaxTime(str) { 28339 var maxValue = 0; 28340 var values = angular.isString(str) ? 28341 str.split(/\s*,\s*/) : 28342 []; 28343 forEach(values, function(value) { 28344 maxValue = Math.max(parseFloat(value) || 0, maxValue); 28345 }); 28346 return maxValue; 28347 } 28348 28349 function getCacheKey(element) { 28350 var parentElement = element.parent(); 28351 var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY); 28352 if(!parentID) { 28353 parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter); 28354 parentID = parentCounter; 28355 } 28356 return parentID + '-' + extractElementNode(element).getAttribute('class'); 28357 } 28358 28359 function animateSetup(animationEvent, element, className, calculationDecorator) { 28360 var cacheKey = getCacheKey(element); 28361 var eventCacheKey = cacheKey + ' ' + className; 28362 var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0; 28363 28364 var stagger = {}; 28365 if(itemIndex > 0) { 28366 var staggerClassName = className + '-stagger'; 28367 var staggerCacheKey = cacheKey + ' ' + staggerClassName; 28368 var applyClasses = !lookupCache[staggerCacheKey]; 28369 28370 applyClasses && element.addClass(staggerClassName); 28371 28372 stagger = getElementAnimationDetails(element, staggerCacheKey); 28373 28374 applyClasses && element.removeClass(staggerClassName); 28375 } 28376 28377 /* the animation itself may need to add/remove special CSS classes 28378 * before calculating the anmation styles */ 28379 calculationDecorator = calculationDecorator || 28380 function(fn) { return fn(); }; 28381 28382 element.addClass(className); 28383 28384 var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {}; 28385 28386 var timings = calculationDecorator(function() { 28387 return getElementAnimationDetails(element, eventCacheKey); 28388 }); 28389 28390 var transitionDuration = timings.transitionDuration; 28391 var animationDuration = timings.animationDuration; 28392 if(transitionDuration === 0 && animationDuration === 0) { 28393 element.removeClass(className); 28394 return false; 28395 } 28396 28397 element.data(NG_ANIMATE_CSS_DATA_KEY, { 28398 running : formerData.running || 0, 28399 itemIndex : itemIndex, 28400 stagger : stagger, 28401 timings : timings, 28402 closeAnimationFn : noop 28403 }); 28404 28405 //temporarily disable the transition so that the enter styles 28406 //don't animate twice (this is here to avoid a bug in Chrome/FF). 28407 var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass'; 28408 if(transitionDuration > 0) { 28409 blockTransitions(element, className, isCurrentlyAnimating); 28410 } 28411 28412 //staggering keyframe animations work by adjusting the `animation-delay` CSS property 28413 //on the given element, however, the delay value can only calculated after the reflow 28414 //since by that time $animate knows how many elements are being animated. Therefore, 28415 //until the reflow occurs the element needs to be blocked (where the keyframe animation 28416 //is set to `none 0s`). This blocking mechanism should only be set for when a stagger 28417 //animation is detected and when the element item index is greater than 0. 28418 if(animationDuration > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) { 28419 blockKeyframeAnimations(element); 28420 } 28421 28422 return true; 28423 } 28424 28425 function isStructuralAnimation(className) { 28426 return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave'; 28427 } 28428 28429 function blockTransitions(element, className, isAnimating) { 28430 if(isStructuralAnimation(className) || !isAnimating) { 28431 extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; 28432 } else { 28433 element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME); 28434 } 28435 } 28436 28437 function blockKeyframeAnimations(element) { 28438 extractElementNode(element).style[ANIMATION_PROP] = 'none 0s'; 28439 } 28440 28441 function unblockTransitions(element, className) { 28442 var prop = TRANSITION_PROP + PROPERTY_KEY; 28443 var node = extractElementNode(element); 28444 if(node.style[prop] && node.style[prop].length > 0) { 28445 node.style[prop] = ''; 28446 } 28447 element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME); 28448 } 28449 28450 function unblockKeyframeAnimations(element) { 28451 var prop = ANIMATION_PROP; 28452 var node = extractElementNode(element); 28453 if(node.style[prop] && node.style[prop].length > 0) { 28454 node.style[prop] = ''; 28455 } 28456 } 28457 28458 function animateRun(animationEvent, element, className, activeAnimationComplete) { 28459 var node = extractElementNode(element); 28460 var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); 28461 if(node.getAttribute('class').indexOf(className) == -1 || !elementData) { 28462 activeAnimationComplete(); 28463 return; 28464 } 28465 28466 var activeClassName = '';
28467 forEach(className.split(' '), function(klass, i) { 28468 activeClassName += (i > 0 ? ' ' : '') + klass + '-active'; 28469 }); 28470 28471 var stagger = elementData.stagger; 28472 var timings = elementData.timings; 28473 var itemIndex = elementData.itemIndex; 28474 var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration); 28475 var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay); 28476 var maxDelayTime = maxDelay * ONE_SECOND; 28477 28478 var startTime = Date.now(); 28479 var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT; 28480 28481 var style = '', appliedStyles = []; 28482 if(timings.transitionDuration > 0) { 28483 var propertyStyle = timings.transitionPropertyStyle; 28484 if(propertyStyle.indexOf('all') == -1) { 28485 style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';'; 28486 style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';'; 28487 appliedStyles.push(CSS_PREFIX + 'transition-property'); 28488 appliedStyles.push(CSS_PREFIX + 'transition-duration'); 28489 } 28490 } 28491 28492 if(itemIndex > 0) { 28493 if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) { 28494 var delayStyle = timings.transitionDelayStyle; 28495 style += CSS_PREFIX + 'transition-delay: ' + 28496 prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; '; 28497 appliedStyles.push(CSS_PREFIX + 'transition-delay'); 28498 } 28499 28500 if(stagger.animationDelay > 0 && stagger.animationDuration === 0) { 28501 style += CSS_PREFIX + 'animation-delay: ' + 28502 prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; '; 28503 appliedStyles.push(CSS_PREFIX + 'animation-delay'); 28504 } 28505 } 28506 28507 if(appliedStyles.length > 0) { 28508 //the element being animated may sometimes contain comment nodes in 28509 //the jqLite object, so we're safe to use a single variable to house 28510 //the styles since there is always only one element being animated 28511 var oldStyle = node.getAttribute('style') || ''; 28512 node.setAttribute('style', oldStyle + '; ' + style); 28513 } 28514 28515 element.on(css3AnimationEvents, onAnimationProgress); 28516 element.addClass(activeClassName); 28517 elementData.closeAnimationFn = function() { 28518 onEnd(); 28519 activeAnimationComplete(); 28520 }; 28521 28522 var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0); 28523 var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER; 28524 var totalTime = (staggerTime + animationTime) * ONE_SECOND; 28525 28526 elementData.running++; 28527 animationCloseHandler(element, totalTime); 28528 return onEnd; 28529 28530 // This will automatically be called by $animate so 28531 // there is no need to attach this internally to the 28532 // timeout done method. 28533 function onEnd(cancelled) { 28534 element.off(css3AnimationEvents, onAnimationProgress); 28535 element.removeClass(activeClassName); 28536 animateClose(element, className); 28537 var node = extractElementNode(element); 28538 for (var i in appliedStyles) { 28539 node.style.removeProperty(appliedStyles[i]); 28540 } 28541 } 28542 28543 function onAnimationProgress(event) { 28544 event.stopPropagation(); 28545 var ev = event.originalEvent || event; 28546 var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now(); 28547 28548 /* Firefox (or possibly just Gecko) likes to not round values up 28549 * when a ms measurement is used for the animation */ 28550 var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); 28551 28552 /* $manualTimeStamp is a mocked timeStamp value which is set 28553 * within browserTrigger(). This is only here so that tests can 28554 * mock animations properly. Real events fallback to event.timeStamp, 28555 * or, if they don't, then a timeStamp is automatically created for them. 28556 * We're checking to see if the timeStamp surpasses the expected delay, 28557 * but we're using elapsedTime instead of the timeStamp on the 2nd 28558 * pre-condition since animations sometimes close off early */ 28559 if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { 28560 activeAnimationComplete(); 28561 } 28562 } 28563 } 28564 28565 function prepareStaggerDelay(delayStyle, staggerDelay, index) { 28566 var style = '';
28567 forEach(delayStyle.split(','), function(val, i) { 28568 style += (i > 0 ? ',' : '') + 28569 (index * staggerDelay + parseInt(val, 10)) + 's'; 28570 }); 28571 return style; 28572 } 28573 28574 function animateBefore(animationEvent, element, className, calculationDecorator) { 28575 if(animateSetup(animationEvent, element, className, calculationDecorator)) { 28576 return function(cancelled) { 28577 cancelled && animateClose(element, className); 28578 }; 28579 } 28580 } 28581 28582 function animateAfter(animationEvent, element, className, afterAnimationComplete) { 28583 if(element.data(NG_ANIMATE_CSS_DATA_KEY)) { 28584 return animateRun(animationEvent, element, className, afterAnimationComplete); 28585 } else { 28586 animateClose(element, className); 28587 afterAnimationComplete(); 28588 } 28589 } 28590 28591 function animate(animationEvent, element, className, animationComplete) { 28592 //If the animateSetup function doesn't bother returning a 28593 //cancellation function then it means that there is no animation 28594 //to perform at all 28595 var preReflowCancellation = animateBefore(animationEvent, element, className); 28596 if(!preReflowCancellation) { 28597 animationComplete(); 28598 return; 28599 } 28600 28601 //There are two cancellation functions: one is before the first 28602 //reflow animation and the second is during the active state 28603 //animation. The first function will take care of removing the 28604 //data from the element which will not make the 2nd animation 28605 //happen in the first place 28606 var cancel = preReflowCancellation; 28607 afterReflow(element, function() { 28608 unblockTransitions(element, className); 28609 unblockKeyframeAnimations(element); 28610 //once the reflow is complete then we point cancel to 28611 //the new cancellation function which will remove all of the 28612 //animation properties from the active animation 28613 cancel = animateAfter(animationEvent, element, className, animationComplete); 28614 }); 28615 28616 return function(cancelled) { 28617 (cancel || noop)(cancelled); 28618 }; 28619 } 28620 28621 function animateClose(element, className) { 28622 element.removeClass(className); 28623 var data = element.data(NG_ANIMATE_CSS_DATA_KEY); 28624 if(data) { 28625 if(data.running) { 28626 data.running--; 28627 } 28628 if(!data.running || data.running === 0) { 28629 element.removeData(NG_ANIMATE_CSS_DATA_KEY); 28630 } 28631 } 28632 } 28633 28634 return { 28635 enter : function(element, animationCompleted) { 28636 return animate('enter', element, 'ng-enter', animationCompleted); 28637 }, 28638 28639 leave : function(element, animationCompleted) { 28640 return animate('leave', element, 'ng-leave', animationCompleted); 28641 }, 28642 28643 move : function(element, animationCompleted) { 28644 return animate('move', element, 'ng-move', animationCompleted); 28645 }, 28646 28647 beforeSetClass : function(element, add, remove, animationCompleted) { 28648 var className = suffixClasses(remove, '-remove') + ' ' + 28649 suffixClasses(add, '-add'); 28650 var cancellationMethod = animateBefore('setClass', element, className, function(fn) { 28651 /* when classes are removed from an element then the transition style 28652 * that is applied is the transition defined on the element without the 28653 * CSS class being there. This is how CSS3 functions outside of ngAnimate. 28654 * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */ 28655 var klass = element.attr('class'); 28656 element.removeClass(remove); 28657 element.addClass(add); 28658 var timings = fn(); 28659 element.attr('class', klass); 28660 return timings; 28661 }); 28662 28663 if(cancellationMethod) { 28664 afterReflow(element, function() { 28665 unblockTransitions(element, className); 28666 unblockKeyframeAnimations(element); 28667 animationCompleted(); 28668 }); 28669 return cancellationMethod; 28670 } 28671 animationCompleted(); 28672 }, 28673 28674 beforeAddClass : function(element, className, animationCompleted) { 28675 var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) { 28676 28677 /* when a CSS class is added to an element then the transition style that 28678 * is applied is the transition defined on the element when the CSS class 28679 * is added at the time of the animation. This is how CSS3 functions 28680 * outside of ngAnimate. */ 28681 element.addClass(className); 28682 var timings = fn(); 28683 element.removeClass(className); 28684 return timings; 28685 }); 28686 28687 if(cancellationMethod) { 28688 afterReflow(element, function() { 28689 unblockTransitions(element, className); 28690 unblockKeyframeAnimations(element); 28691 animationCompleted(); 28692 }); 28693 return cancellationMethod; 28694 } 28695 animationCompleted(); 28696 }, 28697 28698 setClass : function(element, add, remove, animationCompleted) { 28699 remove = suffixClasses(remove, '-remove'); 28700 add = suffixClasses(add, '-add'); 28701 var className = remove + ' ' + add; 28702 return animateAfter('setClass', element, className, animationCompleted); 28703 }, 28704 28705 addClass : function(element, className, animationCompleted) { 28706 return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted); 28707 }, 28708 28709 beforeRemoveClass : function(element, className, animationCompleted) { 28710 var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) { 28711 /* when classes are removed from an element then the transition style 28712 * that is applied is the transition defined on the element without the 28713 * CSS class being there. This is how CSS3 functions outside of ngAnimate. 28714 * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */ 28715 var klass = element.attr('class'); 28716 element.removeClass(className); 28717 var timings = fn(); 28718 element.attr('class', klass); 28719 return timings; 28720 }); 28721 28722 if(cancellationMethod) { 28723 afterReflow(element, function() { 28724 unblockTransitions(element, className); 28725 unblockKeyframeAnimations(element); 28726 animationCompleted(); 28727 }); 28728 return cancellationMethod; 28729 } 28730 animationCompleted(); 28731 }, 28732 28733 removeClass : function(element, className, animationCompleted) { 28734 return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted); 28735 } 28736 }; 28737 28738 function suffixClasses(classes, suffix) { 28739 var className = '';
28740 classes = angular.isArray(classes) ? classes : classes.split(/\s+/); 28741 forEach(classes, function(klass, i) { 28742 if(klass && klass.length > 0) { 28743 className += (i > 0 ? ' ' : '') + klass + suffix; 28744 } 28745 }); 28746 return className; 28747 } 28748 }]); 28749 }]); 28750 28751 28752})(window, window.angular); 28753 28754/*! 28755 * fancyBox - jQuery Plugin 28756 * version: 3.0.0 Beta 1 (Tue, 29 Jan 2013) 28757 * @requires jQuery v1.7 or later 28758 * 28759 * Examples at http://fancyapps.com/fancybox/ 28760 * License: www.fancyapps.com/fancybox/#license 28761 * 28762 * Copyright 2013 Janis Skarnelis - [email protected] 28763 * 28764 */ 28765 28766(function (window, document, $, undefined) { 28767 "use strict"; 28768 28769 var W = $(window), 28770 D = $(document), 28771 H = $('html'); 28772 28773 var F = $.fancybox = function () { 28774 F.open.apply( this, arguments ); 28775 }; 28776 28777 var isTouch = F.isTouch = (document.createTouch !== undefined || window.ontouchstart !== undefined); 28778 28779 var isQuery = function(object) { 28780 return object && object.hasOwnProperty && object instanceof $; 28781 }; 28782 28783 var isString = function(str) { 28784 return str && $.type(str) === "string"; 28785 }; 28786 28787 var isPercentage = function(str) { 28788 return isString(str) && str.indexOf('%') > 0; 28789 }; 28790 28791 var getScalar = function(orig, dim) { 28792 var value = parseFloat(orig, 10) || 0; 28793 28794 if (dim && isPercentage(orig)) { 28795 value = F.getViewport()[ dim ] / 100 * value; 28796 } 28797 28798 return Math.ceil(value); 28799 }; 28800 28801 var getValue = function(value, dim) { 28802 return getScalar(value, dim) + 'px'; 28803 }; 28804 28805 var getTime = Date.now || function() { 28806 return +new Date; 28807 }; 28808 28809 var removeWrap = function(what) { 28810 var el = isString(what) ? $(what) : what; 28811 28812 if (el && el.length) { 28813 el.removeClass('fancybox-wrap').stop(true).trigger('onReset').hide().unbind(); 28814 28815 try { 28816 el.find('iframe').unbind().attr('src', isTouch ? '' : '//about:blank'); 28817 28818 // Give the document in the iframe to get a chance to unload properly before remove 28819 setTimeout(function () { 28820 el.empty().remove(); 28821 28822 // Remove the lock if there are no elements 28823 if (F.lock && !F.coming && !F.current) { 28824 var scrollV, scrollH; 28825 28826 $('.fancybox-margin').removeClass('fancybox-margin'); 28827 28828 scrollV = W.scrollTop(); 28829 scrollH = W.scrollLeft(); 28830 28831 H.removeClass('fancybox-lock'); 28832 28833 F.lock.remove(); 28834 28835 F.lock = null; 28836 28837 W.scrollTop( scrollV ).scrollLeft( scrollH ); 28838 } 28839 }, 150); 28840 28841 } catch(e) {} 28842 } 28843 }; 28844 28845 $.extend(F, { 28846 // The current version of fancyBox 28847 version: '3.0.0', 28848 28849 defaults: { 28850 theme : 'light', // 'default', dark', 'light' 28851 padding : 15, // space inside box, around content 28852 margin : [30, 55, 30, 55], // space between viewport and the box 28853 loop : true, // Continuous gallery item loop 28854 28855 arrows : true, 28856 closeBtn : true, 28857 expander : !isTouch, 28858 28859 caption : { 28860 type : 'outside' // 'float', 'inside', 'outside' or 'over', 28861 }, 28862 28863 overlay : { 28864 closeClick : true, // if true, fancyBox will be closed when user clicks on the overlay 28865 speedIn : 0, // duration of fadeIn animation 28866 speedOut : 250, // duration of fadeOut animation 28867 showEarly : true, // indicates if should be opened immediately or wait until the content is ready 28868 css : {} // custom CSS properties 28869 }, 28870 28871 helpers : {}, // list of enabled helpers 28872 28873 // Dimensions 28874 width : 800, 28875 height : 450, 28876 minWidth : 100, 28877 minHeight : 100, 28878 maxWidth : 99999, 28879 maxHeight : 99999, 28880 aspectRatio : false, 28881 fitToView : true, 28882 28883 autoHeight : true, 28884 autoWidth : true, 28885 autoResize : true, 28886 28887 // Location 28888 autoCenter : !isTouch, 28889 topRatio : 0.5, 28890 leftRatio : 0.5, 28891 28892 // Opening animation 28893 openEffect : 'fade', // 'elastic', 'fade', 'drop' or 'none' 28894 openSpeed : 800, 28895 openEasing : 'easeOutQuad', 28896 28897 // Closing animation 28898 closeEffect : 'fade', // 'elastic', 'fade', 'drop' or 'none' 28899 closeSpeed : 500, 28900 closeEasing : 'easeOutQuad', 28901 28902 // Animation for next gallery item 28903 nextEffect : 'fade', // 'elastic', 'fade', 'drop' or 'none' 28904 nextSpeed : 500, 28905 nextEasing : 'easeOutQuad', 28906 28907 // Animation for previous gallery item 28908 prevEffect : 'fade', // 'elastic', 'fade', 'drop' or 'none' 28909 prevSpeed : 500, 28910 prevEasing : 'easeOutQuad', 28911 28912 // Slideshow 28913 autoPlay : false, 28914 playSpeed : 3000, 28915 28916 /* 28917 Advanced 28918 */ 28919 28920 // Callbacks 28921 onCancel : $.noop, // If canceling 28922 beforeLoad : $.noop, // Before loading 28923 afterLoad : $.noop, // After loading 28924 beforeShow : $.noop, // Before changing in current item 28925 afterShow : $.noop, // After opening 28926 beforeClose : $.noop, // Before closing 28927 afterClose : $.noop, // After closing 28928 28929 // Properties specific to content type 28930 ajax : { 28931 dataType : 'html', 28932 headers : { 'X-fancyBox': true } 28933 }, 28934 28935 iframe : { 28936 scrolling : 'auto', 28937 preload : true 28938 }, 28939 28940 swf : { 28941 wmode : 'transparent', 28942 allowfullscreen : 'true', 28943 allowscriptaccess : 'always' 28944 }, 28945 28946 // Default keyboard 28947 keys : { 28948 next : { 28949 13 : 'left', // enter 28950 34 : 'up', // page down 28951 39 : 'left', // right arrow 28952 40 : 'up' // down arrow 28953 }, 28954 prev : { 28955 8 : 'right', // backspace 28956 33 : 'down', // page up 28957 37 : 'right', // left arrow 28958 38 : 'down' // up arrow 28959 }, 28960 close : [27], // escape key 28961 play : [32], // space - start/stop slideshow 28962 toggle : [70] // letter "f" - toggle fullscreen 28963 }, 28964 28965 // Default direction 28966 direction : { 28967 next : 'left', 28968 prev : 'right' 28969 }, 28970 28971 // HTML templates 28972 tpl: { 28973 wrap : '<div class="fancybox-wrap" tabIndex="-1"><div class="fancybox-inner"></div></div>', 28974 iframe : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true"></iframe>', 28975 error : '<p class="fancybox-error">{{ERROR}}</p>', 28976 closeBtn : '<a title="{{CLOSE}}" class="fancybox-close" href="javascript:;"></a>', 28977 next : '<a title="{{NEXT}}" class="fancybox-nav fancybox-next" href="javascript:;"><span></span></a>', 28978 prev : '<a title="{{PREV}}" class="fancybox-nav fancybox-prev" href="javascript:;"><span></span></a>' 28979 }, 28980 28981 // Localization 28982 locale : 'en', 28983 locales : { 28984 'en' : { 28985 CLOSE : 'Close', 28986 NEXT : 'Next', 28987 PREV : 'Previous', 28988 ERROR : 'The requested content cannot be loaded. <br/> Please try again later.', 28989 EXPAND : 'Display actual size',
28990 SHRINK : 'Fit to the viewport', 28991 PLAY_START : 'Start slideshow', 28992 PLAY_STOP : 'Pause slideshow' 28993 }, 28994 'de' : { 28995 CLOSE : 'Schliessen', 28996 NEXT : 'Vorwärts', 28997 PREV : 'Zurück', 28998 ERROR : 'Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.', 28999 EXPAND : '', 29000 SHRINK : '', 29001 PLAY_START : '', 29002 PLAY_STOP : '' 29003 } 29004 }, 29005 29006 // Override some properties 29007 index : 0, 29008 content : null, 29009 href : null, 29010 29011 // Various 29012 wrapCSS : '', // CSS class name for the box 29013 modal : false, 29014 locked : true, 29015 preload : 3, // Number of gallery images to preload 29016 mouseWheel : true, // Enable or disable mousewheel support 29017 scrolling : 'auto', // 'yes', 'no', any valid value for CSS "overflow" property 29018 scrollOutside : true // If trye, fancyBox will try to set scrollbars outside the content 29019 }, 29020 29021 // Current state 29022 current : null, 29023 coming : null, 29024 group : [], 29025 index : 0, 29026 isActive : false, // Is activated 29027 isOpen : false, // Is currently open 29028 isOpened : false, // Have been fully opened at least once 29029 isMaximized : false, 29030 29031 player : { 29032 timer : null, 29033 isActive : false 29034 }, 29035 29036 // Loaders 29037 ajaxLoad : null, 29038 imgPreload : null, 29039 29040 // Object containing all helpers 29041 helpers : {}, 29042 29043 // Open fancyBox 29044 open: function( items, options ) { 29045 if (!items) { 29046 return; 29047 } 29048 29049 // Close if already active 29050 if (false === F.close(true)) { 29051 return; 29052 } 29053 29054 if (!$.isPlainObject( options )) { 29055 options = {}; 29056 } 29057 29058 F.opts = $.extend(true, {}, F.defaults, options); 29059 29060 F.populate( items ); 29061 29062 if (F.group.length) { 29063 F._start( F.opts.index ); 29064 } 29065 }, 29066 29067 // Add new items to the group 29068 populate : function( items ) { 29069 var group = []; 29070 29071 if ( !$.isArray( items )) { 29072 items = [ items ]; 29073 } 29074 29075 // Build group array, each item is object containing element 29076 // and most important attributes - href, title and type 29077 $.each(items, function(i, element) { 29078 var defaults = $.extend(true, {}, F.opts), 29079 item, 29080 obj, 29081 type, 29082 margin, 29083 padding; 29084 29085 if ($.isPlainObject(element)) { 29086 item = element; 29087 29088 } else if (isString(element)) { 29089 item = { href : element }; 29090 29091 } else if (isQuery(element) || $.type(element) === 'object' && element.nodeType) { 29092 obj = $(element); 29093 item = $(obj).get(0); 29094 29095 if (!item.href) { 29096 item = { href : element }; 29097 } 29098 29099 item = $.extend({ 29100 href : obj.data('fancybox-href') || obj.attr('href') || item.href, 29101 title : obj.data('fancybox-title') || obj.attr('title') || item.title, 29102 type : obj.data('fancybox-type'), 29103 element : obj 29104 }, obj.data('fancybox-options') ); 29105 29106 } else { 29107 return; 29108 } 29109 29110 // If the type has not specified, then try to guess 29111 if (!item.type && (item.content || item.href)) { 29112 item.type = item.content ? "html" : F.guessType( obj, item.href ); 29113 } 29114 29115 // Adjust some defaults depending on content type 29116 type = item.type || F.opts.type; 29117 29118 if (type === 'image' || type === 'swf') { 29119 defaults.autoWidth = defaults.autoHeight = false; 29120 defaults.scrolling = 'visible'; 29121 } 29122 29123 if (type === 'image') { 29124 defaults.aspectRatio = true; 29125 } 29126 29127 if (type === 'iframe') { 29128 defaults.autoWidth = false; 29129 defaults.scrolling = isTouch ? 'scroll' : 'visible'; 29130 } 29131 29132 if (items.length < 2) { 29133 defaults.margin = 30; 29134 } 29135 29136 item = $.extend(true, {}, defaults, item); 29137 29138 // Recheck some parameters 29139 margin = item.margin; 29140 padding = item.padding; 29141 29142 // Convert margin and padding properties to array - top, right, bottom, left 29143 if ($.type(margin) === 'number') { 29144 item.margin = [margin, margin, margin, margin]; 29145 } 29146 29147 if ($.type(padding) === 'number') { 29148 item.padding = [padding, padding, padding, padding]; 29149 } 29150 29151 // 'modal' propery is just a shortcut 29152 if (item.modal) { 29153 $.extend(true, item, { 29154 closeBtn : false, 29155 closeClick : false, 29156 nextClick : false, 29157 arrows : false, 29158 mouseWheel : false, 29159 keys : null, 29160 overlay : { 29161 closeClick : false 29162 } 29163 });
29164 } 29165 29166 if (item.autoSize !== undefined) { 29167 item.autoWidth = item.autoHeight = !!item.autoSize; 29168 } 29169 29170 if (item.width === 'auto') { 29171 item.autoWidth = true; 29172 } 29173 29174 if (item.height === 'auto') { 29175 item.autoHeight = true; 29176 } 29177 29178 group.push( item ); 29179 }); 29180 29181 F.group = F.group.concat( group ); 29182 }, 29183 29184 // Cancel image loading and abort ajax request 29185 cancel: function () { 29186 var coming = F.coming; 29187 29188 if (!coming || false === F.trigger('onCancel')) { 29189 return; 29190 } 29191 29192 F.hideLoading(); 29193 29194 if (F.ajaxLoad) { 29195 F.ajaxLoad.abort(); 29196 } 29197 29198 if (F.imgPreload) { 29199 F.imgPreload.onload = F.imgPreload.onerror = null; 29200 } 29201 29202 if (coming.wrap) { 29203 removeWrap( coming.wrap ); 29204 } 29205 29206 F.ajaxLoad = F.imgPreload = F.coming = null; 29207 29208 // If the first item has been canceled, then clear everything 29209 if (!F.current) { 29210 F._afterZoomOut( coming ); 29211 } 29212 }, 29213 29214 // Start closing or remove immediately if is opening/closing 29215 close: function (e) { 29216 if (e && $.type(e) === 'object') { 29217 e.preventDefault(); 29218 } 29219 29220 F.cancel(); 29221 29222 // Do not close if: 29223 // - the script has not been activated 29224 // - cancel event has triggered opening a new item 29225 // - "beforeClose" trigger has returned false 29226 if (!F.isActive || F.coming || false === F.trigger('beforeClose')) { 29227 return; 29228 } 29229 29230 F.unbind(); 29231 29232 F.isClosing = true; 29233 29234 if (F.lock) { 29235 F.lock.css('overflow', 'hidden'); 29236 } 29237 29238 if (!F.isOpen || e === true) { 29239 F._afterZoomOut(); 29240 29241 } else { 29242 F.isOpen = F.isOpened = false; 29243 29244 F.transitions.close(); 29245 } 29246 }, 29247 29248 prev : function( direction ) { 29249 var current = F.current; 29250 29251 if (current) { 29252 F.jumpto( current.index - 1, (isString(direction) ? direction : current.direction.prev) ); 29253 } 29254 }, 29255 29256 next : function( direction ) { 29257 var current = F.current; 29258 29259 if (current) { 29260 F.jumpto( current.index + 1, (isString(direction) ? direction : current.direction.next) ); 29261 } 29262 }, 29263 29264 jumpto : function( index, direction ) { 29265 var current = F.current; 29266 29267 if (!(F.coming && F.coming.index === index)) { 29268 F.cancel(); 29269 29270 if (current.index == index) { 29271 direction = null; 29272 29273 } else if (!direction) { 29274 direction = current.direction[ index > current.index ? 'next' : 'prev' ]; 29275 } 29276 29277 F.direction = direction; 29278 29279 F._start( index ); 29280 } 29281 } 29282 }); 29283 29284 $.extend(F, { 29285 guessType : function(item, href) { 29286 var rez = item && item.prop('class') ? item.prop('class').match(/fancybox\.(\w+)/) : 0, 29287 type = false; 29288 29289 if (rez) { 29290 return rez[1]; 29291 } 29292 29293 if (isString(href)) { 29294 if (href.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)) { 29295 type = 'image'; 29296 29297 } else if (href.match(/\.(swf)((\?|#).*)?$/i)) { 29298 type = 'swf'; 29299 29300 } else if (href.charAt(0) === '#') { 29301 type = 'inline'; 29302 } 29303 29304 } else if (isString(item)) { 29305 type = 'html'; 29306 } 29307 29308 return type; 29309 }, 29310 29311 trigger: function (event, o) { 29312 var ret, obj = o || F.coming || F.current; 29313 29314 if (!obj) { 29315 return; 29316 } 29317 29318 if ($.isFunction( obj[event] )) { 29319 ret = obj[event].apply(obj, Array.prototype.slice.call(arguments, 1)); 29320 } 29321 29322 // Cancel further execution if afterClose callback has opened new instance 29323 if (ret === false || (event === 'afterClose' && F.isActive) ) { 29324 return false; 29325 } 29326 29327 if (obj.helpers) { 29328 $.each(obj.helpers, function (helper, opts) { 29329 var helperObject = F.helpers[helper], 29330 helperOpts; 29331 29332 if (opts && helperObject && $.isFunction(helperObject[event])) { 29333 helperOpts = $.extend(true, {}, helperObject.defaults, opts); 29334 29335 helperObject.opts = helperOpts; 29336 29337 helperObject[event](helperOpts, obj ); 29338 } 29339 }); 29340 } 29341 29342 $.event.trigger(event); 29343 }, 29344 29345 // Center inside viewport 29346 reposition: function (e, object) { 29347 var obj = object || F.current, 29348 wrap = obj && obj.wrap, 29349 pos; 29350 29351 if (F.isOpen && wrap) { 29352 pos = F._getPosition( obj ); 29353 29354 if (e === false || (e && e.type === 'scroll')) { 29355 wrap.stop(true).animate(pos, 200).css('overflow', 'visible'); 29356 29357 } else { 29358 wrap.css(pos); 29359 } 29360 } 29361 }, 29362 29363 update: function (e) { 29364 var type = (e && e.type), 29365 timeNow = getTime(), 29366 current = F.current, 29367 width; 29368 29369 if (!current || !F.isOpen ) { 29370 return; 29371 } 29372 29373 if (type === 'scroll') { 29374 if (F.wrap.outerHeight(true) > F.getViewport().h) { 29375 return; 29376 } 29377 29378 if (F.didUpdate) { 29379 clearTimeout( F.didUpdate ); 29380 } 29381 29382 F.didUpdate = setTimeout(function() { 29383 F.reposition(e); 29384 29385 F.didUpdate = null; 29386 }, 50); 29387 29388 return; 29389 } 29390 29391 if (F.lock) { 29392 F.lock.css('overflow', 'hidden'); 29393 } 29394 29395 F._setDimension(); 29396 29397 F.reposition(e); 29398 29399 if (F.lock) { 29400 F.lock.css('overflow', 'auto'); 29401 } 29402 29403 // Re-center float type caption 29404 if (current.caption.type === 'float') { 29405 width = F.getViewport().w - (F.wrap.outerWidth(true) - F.inner.width() ); 29406 29407 current.caption.wrap.css('width', width).css('marginLeft', (width * 0.5 - F.inner.width() * 0.5) * -1 ); 29408 } 29409 29410 if (current.expander) { 29411 if ( current.canShrink) { 29412 $(".fancybox-expand").show().attr('title', current.locales[ current.locale ].SHRINK ); 29413 29414 } else if (current.canExpand) { 29415 $(".fancybox-expand").show().attr('title', current.locales[ current.locale ].EXPAND ); 29416 29417 } else { 29418 $(".fancybox-expand").hide(); 29419 } 29420 } 29421 29422 F.trigger('onUpdate'); 29423 }, 29424 29425 // Shrink content to fit inside viewport or restore if resized 29426 toggle: function ( action ) { 29427 var current = F.current; 29428 29429 if (current && F.isOpen) { 29430 F.current.fitToView = $.type(action) === "boolean" ? action : !F.current.fitToView; 29431 29432 F.update( true ); 29433 } 29434 }, 29435 29436 hideLoading: function () { 29437 $('#fancybox-loading').remove(); 29438 }, 29439 29440 showLoading: function () { 29441 var el, view; 29442 29443 F.hideLoading(); 29444 29445 el = $('<div id="fancybox-loading"></div>').click(F.cancel).appendTo('body'); 29446 29447 if (!F.defaults.fixed) { 29448 view = F.getViewport(); 29449 29450 el.css({ 29451 position : 'absolute', 29452 top : (view.h * 0.5) + view.y, 29453 left : (view.w * 0.5) + view.x 29454 }); 29455 } 29456 }, 29457 29458 getViewport: function () { 29459 var rez; 29460 29461 if (F.lock) { 29462 rez = { 29463 x: F.lock.scrollLeft(), 29464 y: F.lock.scrollTop(), 29465 w: F.lock[0].clientWidth, 29466 h: F.lock[0].clientHeight 29467 }; 29468 29469 } else { 29470 rez = { 29471 x: W.scrollLeft(), 29472 y: W.scrollTop(), 29473 29474 // See http://bugs.jquery.com/ticket/6724 29475 w : isTouch && window.innerWidth ? window.innerWidth : W.width(), 29476 h : isTouch && window.innerHeight ? window.innerHeight : W.he
29476ight() 29477 }; 29478 } 29479 29480 return rez; 29481 }, 29482 29483 unbind : function() { 29484 if (isQuery(F.wrap)) { 29485 F.wrap.unbind('.fb'); 29486 } 29487 29488 if (isQuery(F.inner)) { 29489 F.inner.unbind('.fb'); 29490 } 29491 29492 D.unbind('.fb'); 29493 W.unbind('.fb'); 29494 }, 29495 29496 rebind: function () { 29497 var current = F.current, 29498 keys; 29499 29500 F.unbind(); 29501 29502 if (!current || !F.isOpen) { 29503 return; 29504 } 29505 29506 // Changing document height on iOS devices triggers a 'resize' event, 29507 // that can change document height... repeating infinitely 29508 W.bind('orientationchange.fb' + (isTouch ? '' : ' resize.fb') + (current.autoCenter && !current.locked ? ' scroll.fb' : ''), F.update); 29509 29510 keys = current.keys; 29511 29512 if (keys) { 29513 D.bind('keydown.fb', function (e) { 29514 var code = e.which || e.keyCode, 29515 target = e.target || e.srcElement; 29516 29517 // Skip esc key if loading, because showLoading will cancel preloading 29518 if (code === 27 && F.coming) { 29519 return false; 29520 } 29521 29522 // Ignore key combinations and key events within form elements 29523 if (!e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !(target && (target.type || $(target).is('[contenteditable]')))) { 29524 $.each(keys, function(i, val) { 29525 //if (current.group.length > 1 && val[ code ] !== undefined) { 29526 if (val[ code ] !== undefined) { 29527 e.preventDefault(); 29528 29529 if (current.group.length > 1) { 29530 F[ i ]( val[ code ] ); 29531 } 29532 29533 return false; 29534 } 29535 29536 if ($.inArray(code, val) > -1) { 29537 e.preventDefault(); 29538 29539 if (i === 'play') { 29540 F.slideshow.toggle(); 29541 } else { 29542 F[ i ] (); 29543 } 29544 29545 return false; 29546 } 29547 }); 29548 } 29549 }); 29550 } 29551 29552 F.lastScroll = getTime(); 29553 29554 if (current.mouseWheel && F.group.length > 1) { 29555 F.wrap.bind('DOMMouseScroll.fb mousewheel.fb MozMousePixelScroll.fb', function (event) { 29556 var e = event.originalEvent, 29557 el = e.target || 0, 29558 delta = (e.wheelDelta || e.detail || 0), 29559 deltaX = e.wheelDeltaX || 0, 29560 deltaY = e.wheelDeltaY || 0, 29561 now = getTime(); 29562 29563 if (((el && el.style && !(el.style.overflow && el.style.overflow === 'hidden') && ((el.clientWidth && el.scrollWidth > el.clientWidth) || (el.clientHeight && el.scrollHeight > el.clientHeight)))) ) { 29564 return; 29565 } 29566 29567 if (delta === 0 || (F.current && F.current.canShrink)) { 29568 return; 29569 } 29570 29571 e.stopPropagation(); 29572 29573 if (F.lastScroll && (now - F.lastScroll) < 80) { 29574 F.lastScroll = now; 29575 return; 29576 } 29577 29578 F.lastScroll = now; 29579 29580 if (e.axis) { 29581 if (e.axis === e.HORIZONTAL_AXIS) { 29582 deltaX = delta * -1; 29583 29584 } else if (e.axis === e.VERTICAL_AXIS) { 29585 deltaY = delta * -1; 29586 } 29587 } 29588 29589 if ( deltaX === 0 ) { 29590 if (deltaY > 0) { 29591 F.prev( 'down' ); 29592 29593 } else { 29594 F.next( 'up' ); 29595 } 29596 29597 } else { 29598 if (deltaX > 0) { 29599 F.prev( 'right' ); 29600 29601 } else { 29602 F.next( 'left' ); 29603 } 29604 } 29605 }); 29606 } 29607 29608 F.touch.init(); 29609 }, 29610 29611 rebuild : function() { 29612 var current = F.current; 29613 29614 current.wrap.find('.fancybox-nav, .fancybox-close, .fancybox-expand').remove(); 29615 29616 // Create navigation arrows 29617 if (current.arrows && F.group.length > 1) { 29618 if (current.loop || current.index > 0) { 29619 $( F._translate( current.tpl.prev) ).appendTo(F.inner).bind('click.fb', F.prev); 29620 } 29621 29622 if (current.loop || current.index < F.group.length - 1) { 29623 $( F._translate( current.tpl.next) ).appendTo(F.inner).bind('click.fb', F.next); 29624 } 29625 } 29626 29627 // Create a close button 29628 if (current.closeBtn) { 29629 $( F._translate( current.tpl.closeBtn) ).appendTo(F.wrap).bind('click.fb', F.close); 29630 } 29631 29632 // Add expand button to image 29633 if (current.expander && current.type === 'image') { 29634 $('<a title="Expand image" class="fancybox-expand" href="javascript:;"></a>') 29635 .appendTo( F.inner ) 29636 .bind('click.fb', F.toggle); 29637 29638 if ( !current.canShrink && !current.canExpand) { 29639 29640 } 29641 } 29642 }, 29643 29644 // Create upcoming object and prepare for loading the content 29645 _start: function( index ) { 29646 var coming, 29647 type; 29648 29649 // Check index and get object from the groups 29650 if (F.opts.loop) { 29651 if (index < 0) { 29652 index = F.group.length + (index % F.group.length); 29653 } 29654 29655 index = index % F.group.length; 29656 } 29657 29658 coming = F.group[ index ]; 29659 29660 if (!coming) { 29661 return false; 29662 } 29663 29664 // Add all properties 29665 coming = $.extend(true, {}, F.opts, coming); 29666 29667 /* 29668 * Add reference to the group, so it`s possible to access from callbacks, example: 29669 * afterLoad : function() { 29670 * this.title = 'Image ' + (this.index + 1) + ' of ' + this.group.length + (this.title ? ' - ' + this.title : ''); 29671 * } 29672 */ 29673 29674 coming.group = F.group;
29675 coming.index = index; 29676 29677 // Give a chance for callback or helpers to update coming item (type, title, etc) 29678 F.coming = coming; 29679 29680 if (false === F.trigger('beforeLoad')) { 29681 F.coming = null; 29682 29683 return; 29684 } 29685 29686 F.isActive = true; 29687 29688 // Build the neccessary markup 29689 F._build(); 29690 29691 // If user will press the escape-button, the request will be canceled 29692 D.bind('keydown.loading', function(e) { 29693 if ((e.which || e.keyCode) === 27) { 29694 D.unbind('.loading'); 29695 29696 e.preventDefault(); 29697 29698 F.cancel(); 29699 } 29700 }); 29701 29702 // Show overlay 29703 if (coming.overlay && coming.overlay.showEarly) { 29704 F.overlay.open( coming.overlay ); 29705 } 29706 29707 // Load content 29708 type = coming.type; 29709 29710 if (type === 'image') { 29711 F._loadImage(); 29712 29713 } else if (type === 'ajax') { 29714 F._loadAjax(); 29715 29716 } else if (type === 'iframe') { 29717 F._loadIframe(); 29718 29719 } else if (type === 'inline') { 29720 F._loadInline(); 29721 29722 } else if (type === 'html' || type === 'swf') { 29723 F._afterLoad(); 29724 29725 } else { 29726 F._error(); 29727 } 29728 }, 29729 29730 _build : function() { 29731 var coming = F.coming, 29732 captionType = coming.caption.type, 29733 wrap, 29734 inner, 29735 scrollV, 29736 scrollH; 29737 29738 coming.wrap = wrap = $('<div class="fancybox-wrap"></div>').appendTo( coming.parent || 'body' ).addClass('fancybox-' + coming.theme);
29739 coming.inner = inner = $('<div class="fancybox-inner"></div>').appendTo( wrap ); 29740 29741 coming[ captionType === 'outside' || captionType === 'float' ? 'inner' : 'wrap' ].addClass('fancybox-skin fancybox-' + coming.theme + '-skin'); 29742 29743 if (coming.locked && coming.overlay && F.defaults.fixed) { 29744 if (!F.lock) { 29745 F.lock = $('<div id="fancybox-lock"></div>').appendTo( wrap.parent() ); 29746 } 29747 29748 F.lock.unbind().append( wrap ); 29749 29750 if (coming.overlay.closeClick) { 29751 F.lock.click(function(e) { 29752 if ($(e.target).is(F.lock)) { 29753 F.close(); 29754 } 29755 }); 29756 } 29757 29758 // Compensate missing page scrolling by increasing margin 29759 if (D.height() > W.height() || H.css('overflow-y') === 'scroll') { 29760 $('*:visible').filter(function(){ 29761 return ($(this).css('position') === 'fixed' && !$(this).hasClass("fancybox-overlay") && $(this).attr('id') !== "fancybox-lock"); 29762 }).addClass('fancybox-margin'); 29763 29764 H.addClass('fancybox-margin'); 29765 } 29766 29767 // Workaround for FF jumping bug 29768 scrollV = W.scrollTop(); 29769 scrollH = W.scrollLeft(); 29770 29771 H.addClass('fancybox-lock'); 29772 29773 W.scrollTop( scrollV ).scrollLeft( scrollH ); 29774 } 29775 29776 F.trigger('onReady'); 29777 }, 29778 29779 _error: function ( type ) { 29780 if (!F.coming) { 29781 return; 29782 } 29783 29784 $.extend(F.coming, { 29785 type : 'html', 29786 autoWidth : true, 29787 autoHeight : true, 29788 closeBtn : true, 29789 minWidth : 0, 29790 minHeight : 0, 29791 padding : [15, 15, 15, 15], 29792 scrolling : 'visible', 29793 hasError : type, 29794 content : F._translate( F.coming.tpl.error ) 29795 }); 29796 29797 F._afterLoad(); 29798 }, 29799 29800 _loadImage: function () { 29801 // Reset preload image so it is later possible to check "complete" property 29802 var img = F.imgPreload = new Image(); 29803 29804 img.onload = function () { 29805 this.onload = this.onerror = null; 29806 29807 $.extend(F.coming, { 29808 width : this.width, 29809 height : this.height, 29810 content : $(this).addClass('fancybox-image') 29811 }); 29812 29813 F._afterLoad(); 29814 }; 29815 29816 img.onerror = function () { 29817 this.onload = this.onerror = null; 29818 29819 F._error( 'image' ); 29820 }; 29821 29822 img.src = F.coming.href; 29823 29824 if (img.complete !== true || img.width < 1) { 29825 F.showLoading(); 29826 } 29827 }, 29828 29829 _loadAjax: function () { 29830 var coming = F.coming, 29831 href = coming.href, 29832 hrefParts, 29833 selector; 29834 29835 hrefParts = href.split(/\s+/, 2); 29836 href = hrefParts.shift(); 29837 selector = hrefParts.shift(); 29838 29839 F.showLoading(); 29840 29841 F.ajaxLoad = $.ajax($.extend({}, coming.ajax, { 29842 url: coming.href, 29843 error: function (jqXHR, textStatus) { 29844 if (F.coming && textStatus !== 'abort') { 29845 F._error( 'ajax', jqXHR ); 29846 29847 } else { 29848 F.hideLoading(); 29849 } 29850 }, 29851 success: function (data, textStatus) { 29852 if (textStatus === 'success') { 29853 if (selector) { 29854 data = $('<div>').html(data).find(selector); 29855 } 29856 29857 coming.content = data; 29858 29859 F._afterLoad(); 29860 } 29861 } 29862 })); 29863 }, 29864 29865 _loadIframe: function() { 29866 var coming = F.coming, 29867 iframe; 29868 29869 coming.content = iframe = $(coming.tpl.iframe.replace(/\{rnd\}/g, new Date().getTime())) 29870 .attr('scrolling', isTouch ? 'auto' : coming.iframe.scrolling); 29871 29872 if (coming.iframe.preload) { 29873 F.showLoading(); 29874 29875 F._setDimension( coming ); 29876 29877 coming.wrap.addClass('fancybox-tmp'); 29878 29879 iframe.one('load.fb', function() { 29880 if (coming.iframe.preload) { 29881 $(this).data('ready', 1); 29882 29883 $(this).bind('load.fb', F.update); 29884 29885 F._afterLoad(); 29886 } 29887 }); 29888 } 29889 29890 iframe.attr('src', coming.href).appendTo(coming.inner); 29891 29892 if (!coming.iframe.preload) { 29893 F._afterLoad(); 29894 29895 } else if (iframe.data('ready') !== 1) { 29896 F.showLoading(); 29897 } 29898 }, 29899 29900 _loadInline : function() { 29901 var coming = F.coming, 29902 href = coming.href; 29903 29904 coming.content = $( isString(href) ? href.replace(/.*(?=#[^\s]+$)/, '') : href ); //strip for ie7 29905 29906 if (coming.content.length) { 29907 F._afterLoad(); 29908 29909 } else { 29910 F._error(); 29911 } 29912 }, 29913 29914 _preloadImages: function() { 29915 var group = F.group, 29916 current = F.current, 29917 len = group.length, 29918 cnt = current.preload ? Math.min(current.preload, len - 1) : 0, 29919 item, 29920 i; 29921 29922 for (i = 1; i <= cnt; i += 1) { 29923 item = group[ (current.index + i ) % len ]; 29924 29925 if (item && item.type === 'image' && item.href) { 29926 new Image().src = item.href; 29927 } 29928 } 29929 }, 29930 29931 _afterLoad : function() { 29932 var current = F.coming, 29933 previous = F.current; 29934 29935 D.unbind('.loading'); 29936 29937 if (!current || F.isActive === false || false === F.trigger('afterLoad', current, previous)) { 29938 F.hideLoading(); 29939 29940 if (current && current.wrap) { 29941 removeWrap( current.wrap ); 29942 } 29943 29944 if (!previous) { 29945 F._afterZoomOut( current ); 29946 } 29947 29948 F.coming = null; 29949 29950 return; 29951 } 29952 29953 $.extend(F, { 29954 wrap : current.wrap.addClass('fancybox-type-' + current.type + ' fancybox-' + (isTouch ? 'mobile' : 'desktop') + ' fancybox-' + current.theme + '-' + (isTouch ? 'mobile' : 'desktop') + ' ' + current.wrapCSS), 29955 inner : current.inner, 29956 current : current, 29957 previous : previous 29958 }); 29959 29960 // Set content, margin/padding, caption, etc 29961 F._prepare(); 29962 29963 // Give a chance for helpers or callbacks to update elements 29964 F.trigger('beforeShow', current, previous); 29965 29966 F.isOpen = false; 29967 F.coming = null; 29968 29969 // Set initial dimension 29970 F._setDimension(); 29971 29972 F.hideLoading(); 29973 29974 // Open overlay if is not yet open 29975 if (current.overlay && !F.overlay.el) { 29976 F.overlay.open( current.overlay ); 29977 } 29978 29979 F.transitions.open(); 29980 }, 29981 29982 _prepare : function() { 29983 var current = F.current, 29984 content = current.content || '', 29985 wrap = current.wrap, 29986 inner = current.inner, 29987 margin = current.margin, 29988 padding = current.padding, 29989 href = current.href, 29990 type = current.type, 29991 scrolling = current.scrolling, 29992 caption = current.caption, 29993 captionText = current.title, 29994 captionType = caption.type, 29995 placeholder = 'fancybox-placeholder', 29996 display = 'fancybox-display', 29997 embed; 29998
29999 if (type !== 'iframe' && isQuery(content) && content.length) { 30000 if (!content.data(placeholder)) { 30001 content.data(display, content.css('display')) 30002 .data(placeholder, $('<div class="' + placeholder + '"></div>').insertAfter( content ).hide() ); 30003 } 30004 30005 content = content.show().detach(); 30006 30007 current.wrap.bind('onReset', function () { 30008 if ($(this).find(content).length) { 30009 content.css('display', content.data(display)) 30010 .replaceAll( content.data(placeholder) ) 30011 .data(placeholder, false) 30012 .data(display, false); 30013 } 30014 }); 30015 } 30016 30017 if (type === 'swf') { 30018 content = '<object id="fancybox-swf" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"><param name="movie" value="' + href + '"></param>'; 30019 embed = ''; 30020 30021 $.each(current.swf, function(name, val) { 30022 content += '<param name="' + name + '" value="' + val + '"></param>'; 30023 embed += ' ' + name + '="' + val + '"'; 30024 }); 30025 30026 content += '<embed src="' + href + '" type="application/x-shockwave-flash" width="100%" height="100%"' + embed + '></embed></object>'; 30027 } 30028 30029 if (!(isQuery(content) && content.parent().is(current.inner))) { 30030 current.inner.append( content ); 30031 30032 current.content = current.inner.children(':last'); 30033 } 30034 30035 // Add margin / padding 30036 $.each(["Top", "Right", "Bottom", "Left"], function(i, v) { 30037 if (margin[ i ]) { 30038 wrap.css('margin' + v, getValue(margin[ i ])); 30039 } 30040 30041 if (padding[ i ]) { 30042 if (!(v === 'Bottom' && captionType === 'outside')) { 30043 wrap.css('padding' + v, getValue(padding[ i ]) ); 30044 } 30045 30046 if (captionType === 'outside' || captionType === 'float') { 30047 30048 inner.css('border' + v + 'Width', getValue(padding[ i ])); 30049 30050 if (v === 'Top' || v === 'Left') { 30051 inner.css('margin' + v, getValue(padding[ i ] * -1)); 30052 } 30053 } 30054 } 30055 }); 30056 30057 // Add caption 30058 if ($.isFunction(captionText)) { 30059 captionText = captionText.call(current.element, current); 30060 } 30061 30062 if (isString(captionText) && $.trim(captionText) !== '') { 30063 current.caption.wrap = $('<div class="fancybox-title fancybox-title-' + captionType + '-wrap">' + captionText + '</div>').appendTo( current[ captionType === 'over' ? 'inner' : 'wrap' ] ); 30064 30065 if (captionType === 'float') { 30066 current.caption.wrap.width( F.getViewport().w - (F.wrap.outerWidth(true) - F.inner.width() ) ).wrapInner('<div></div>'); 30067 } 30068 } 30069 }, 30070 30071 _setDimension: function( object ) { 30072 var view = F.getViewport(), 30073 current = object || F.current, 30074 wrap = current.wrap, 30075 inner = current.inner, 30076 width = current.width, 30077 height = current.height, 30078 minWidth = current.minWidth, 30079 minHeight = current.minHeight, 30080 maxWidth = current.maxWidth, 30081 maxHeight = current.maxHeight, 30082 margin = current.margin, 30083 scrollOut = current.scrollOutside ? current.scrollbarWidth : 0, 30084 margin = current.margin, 30085 padding = current.padding, 30086 scrolling = current.scrolling, 30087 steps = 1, 30088 scrollingX, 30089 scrollingY, 30090 hSpace, 30091 wSpace, 30092 origWidth, 30093 origHeight, 30094 ratio, 30095 iframe, 30096 body, 30097 maxWidth_, 30098 maxHeight_, 30099 width_, 30100 height_, 30101 canShrink, 30102 canExpand; 30103 30104 // Set scrolling 30105 scrolling = scrolling.split(','); 30106 scrollingX = scrolling[0]; 30107 scrollingY = scrolling[1] || scrollingX; 30108 30109 current.inner 30110 .css('overflow-x', scrollingX === 'yes' ? 'scroll' : (scrollingX === 'no' ? 'hidden' : scrollingX)) 30111 .css('overflow-y', scrollingY === 'yes' ? 'scroll' : (scrollingY === 'no' ? 'hidden' : scrollingY)); 30112 30113 wSpace = margin[1] + margin[3] + padding[1] + padding[3]; 30114 hSpace = margin[0] + margin[2] + padding[0] + padding[2]; 30115 30116 // Calculations for the content 30117 minWidth = getScalar( isPercentage(minWidth) ? getScalar(minWidth, 'w') - wSpace : minWidth ); 30118 maxWidth = getScalar( isPercentage(maxWidth) ? getScalar(maxWidth, 'w') - wSpace : maxWidth ); 30119 30120 minHeight = getScalar( isPercentage(minHeight) ? getScalar(minHeight, 'h') - hSpace : minHeight ); 30121 maxHeight = getScalar( isPercentage(maxHeight) ? getScalar(maxHeight, 'h') - hSpace : maxHeight ); 30122 30123 origWidth = getScalar( isPercentage(width) ? getScalar(width, 'w') - wSpace : width ); 30124 origHeight = getScalar( isPercentage(height) ? getScalar(height, 'h') - hSpace : height ); 30125 30126 if (current.fitToView) { 30127 maxWidth = Math.min(maxWidth, getScalar('100%', 'w') - wSpace ); 30128 maxHeight = Math.min(maxHeight, getScalar('100%', 'h') - hSpace ); 30129 } 30130 30131 maxWidth_ = view.w; 30132 maxHeight_ = view.h; 30133 30134 if (current.type === 'iframe') { 30135 iframe = current.content; 30136 30137 wrap.removeClass('fancybox-tmp'); 30138 30139 if ((current.autoWidth || current.autoHeight) && iframe && iframe.data('ready') === 1) { 30140 30141 try { 30142 if (iframe[0].contentWindow && iframe[0].contentWindow.document.location) { 30143 body = iframe.contents().find('body'); 30144 30145 inner.addClass( 'fancybox-tmp' ); 30146 30147 inner.width( screen.width - wSpace ).height( 99999 ); 30148 30149 if (scrollOut) { 30150 body.css('overflow-x', 'hidden'); 30151 } 30152 30153 if (current.autoWidth) { 30154 origWidth = body.outerWidth(true); 30155 } 30156 30157 if (current.autoHeight) { 30158 origHeight = body.outerHeight(true); 30159 } 30160 30161 inner.removeClass( 'fancybox-tmp' ); 30162 } 30163 30164 } catch (e) {} 30165 } 30166 30167 } else if ( (current.autoWidth || current.autoHeight) && !(current.type === 'image' || current.type === 'swf') ) { 30168 inner.addClass( 'fancybox-tmp' ); 30169 30170 // Set width or height in case we need to calculate only one dimension 30171 if (current.autoWidth) { 30172 inner.width( 'auto' ); 30173 30174 } else { 30175 inner.width( maxWidth ); 30176 } 30177 30178 if (current.autoHeight) { 30179 inner.height( 'auto' ); 30180 30181 } else { 30182 inner.height( maxHeight ); 30183 } 30184 30185 if (current.autoWidth) { 30186 origWidth = inner[0].scrollWidth || inner.width(); 30187 } 30188 30189 if (current.autoHeight) { 30190 origHeight = inner[0].scrollHeight || inner.height(); 30191 } 30192 30193 inner.removeClass( 'fancybox-tmp' ); 30194 } 30195 30196 width = origWidth; 30197 height = origHeight; 30198 ratio = origWidth / origHeight; 30199 30200 if (!current.autoResize) { 30201 wrap.css({ 30202 width : getValue( width ), 30203 height : 'auto' 30204 }); 30205 30206 inner.css({ 30207 width : getValue( width ), 30208 height : getValue( height ) 30209 }); 30210 return; 30211 } 30212 30213 if (current.aspectRatio) { 30214 if (width > maxWidth) { 30215 width = maxWidth; 30216 height = width / ratio; 30217 } 30218 30219 if (height > maxHeight) { 30220 height = maxHeight; 30221 width = height * ratio; 30222 } 30223 30224 if (width < minWidth) { 30225 width = minWidth; 30226 height = width / ratio; 30227 } 30228 30229 if (height < minHeight) { 30230 height = minHeight; 30231 width = height * ratio; 30232 } 30233 30234 } else { 30235 width = Math.max(minWidth, Math.min(width, maxWidth)); 30236
30237 if (current.autoHeight && current.type !== 'iframe') { 30238 inner.width( width ); 30239 30240 origHeight = height = inner[0].scrollHeight; 30241 } 30242 30243 height = Math.max(minHeight, Math.min(height, maxHeight)); 30244 } 30245 30246 // Wrap element has to have fixed width, because long title can expand it 30247 wrap.css({ 30248 width : getValue( width ), 30249 height : 'auto' 30250 }); 30251 30252 inner.css({ 30253 width : getValue( width ), 30254 height : getValue( height ) 30255 }); 30256 30257 width_ = getScalar( wrap.outerWidth(true) ); 30258 height_ = getScalar( wrap.outerHeight(true) ); 30259 30260 if (current.fitToView) { 30261 // Since we do not know how many lines will be at the final, we need to 30262 // resize box till it fits inside max dimensions 30263 if (current.aspectRatio) { 30264 while ((width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight) { 30265 if (steps++ > 30) { 30266 break; 30267 } 30268 30269 height = Math.max(minHeight, Math.min(maxHeight, height - 10)); 30270 width = getScalar(height * ratio); 30271 30272 if (width < minWidth) { 30273 width = minWidth; 30274 height = getScalar(width / ratio); 30275 } 30276 30277 if (width > maxWidth) { 30278 width = maxWidth; 30279 height = getScalar(width / ratio); 30280 } 30281 30282 wrap.css({ 30283 width : getValue( width ) 30284 }); 30285 30286 inner.css({ 30287 width : getValue( width ), 30288 height : getValue( height ) 30289 }); 30290 30291 width_ = getScalar( wrap.outerWidth(true) ); 30292 height_ = getScalar( wrap.outerHeight(true) ); 30293 } 30294 30295 } else { 30296 width = Math.max(minWidth, Math.min(width, width - (width_ - maxWidth_ ))); 30297 height = Math.max(minHeight, Math.min(height, height - (height_ - maxHeight_ ))); 30298 } 30299 } 30300 30301 30302 if (scrollOut && scrollingX === 'auto' && (height < inner[0].scrollHeight || (isQuery(current.content) && current.content[0] && height < current.content[0].offsetHeight)) && (width + wSpace + scrollOut) < maxWidth) { 30303 width += scrollOut; 30304 } 30305 30306 wrap.css({ 30307 width : width 30308 }); 30309 30310 inner.css({ 30311 width : getValue( width ), 30312 height : getValue( height ) 30313 }); 30314 30315 width_ = getScalar( wrap.outerWidth(true) ); 30316 height_ = getScalar( wrap.outerHeight(true) ); 30317 30318 canShrink = (width_ > maxWidth_ || height_ > maxHeight_) && width > minWidth && height > minHeight; 30319 canExpand = (width_ < maxWidth_ || height_ < maxHeight_) && ( current.aspectRatio ? (width < maxWidth && height < maxHeight && width < origWidth && height < origHeight) : ((width < maxWidth || height < maxHeight) && (width < origWidth || height < origHeight)) ); 30320 30321 current.canShrink = canShrink; 30322 current.canExpand = canExpand; 30323 30324 if (!iframe && current.autoHeight && height > minHeight && height < maxHeight && !canExpand) { 30325 inner.height('auto'); 30326 } 30327 }, 30328 30329 _getPosition: function( object ) { 30330 var obj = object || F.current, 30331 wrap = obj.wrap, 30332 view = F.getViewport(), 30333 pos = {}, 30334 top = view.y, 30335 left = view.x; 30336 30337 pos = { 30338 top : getValue( Math.max(top, top + ((view.h - wrap.outerHeight(true)) * obj.topRatio)) ), 30339 left : getValue( Math.max(left, left + ((view.w - wrap.outerWidth(true)) * obj.leftRatio)) ), 30340 width : getValue( wrap.width() ), 30341 height : getValue( wrap.height() ) 30342 }; 30343 30344 return pos; 30345 }, 30346 30347 _afterZoomIn : function() { 30348 var current = F.current; 30349 30350 if (!current) { 30351 return; 30352 } 30353 30354 if (F.lock) { 30355 F.lock.css('overflow', 'auto'); 30356 } 30357 30358 F.isOpen = F.isOpened = true; 30359 30360 F.rebuild(); 30361 30362 F.rebind(); 30363 30364 if (current.caption && current.caption.wrap) { 30365 current.caption.wrap.show().css({ 30366 'visibility' : 'visible', 30367 'opacity' : 0, 30368 'left' : 0 30369 }) 30370 .animate({opacity:1}, "fast"); 30371 } 30372 30373 F.update(); 30374 30375 F.wrap.css('overflow', 'visible').addClass('fancybox-open').focus(); 30376 30377 F[ F.wrap.hasClass('fancybox-skin') ? 'wrap' : 'inner' ].addClass('fancybox-' + current.theme + '-skin-open'); 30378 30379 if (current.caption && current.caption.wrap) { 30380 current.caption.wrap.show().css('left', 0).animate({opacity:1}, "fast"); 30381 } 30382 30383 // Add empty element to simulate bottom margin, this trick helps to avoid extra element 30384 if (current.margin[2] > 0) { 30385 $('<div class="fancybox-spacer"></div>').css('height', getValue( current.margin[2] - 2) ).appendTo( F.wrap ); 30386 } 30387 30388 F.trigger('afterShow'); 30389 30390 F._preloadImages(); 30391 30392 if (current.autoPlay && !F.slideshow.isActive) { 30393 F.slideshow.start(); 30394 } 30395 }, 30396 30397 _afterZoomOut : function(obj) { 30398 var cleanup = function() { 30399 removeWrap('.fancybox-wrap'); 30400 }; 30401 30402 F.hideLoading(); 30403 30404 obj = obj || F.current; 30405 30406 if (obj && obj.wrap) { 30407 obj.wrap.hide(); 30408 } 30409 30410 $.extend(F, { 30411 group : [], 30412 opts : {}, 30413 coming : null, 30414 current : null, 30415 isActive : false, 30416 isOpened : false, 30417 isOpen : false, 30418 isClosing : false, 30419 wrap : null, 30420 skin : null, 30421 inner : null 30422 }); 30423 30424 F.trigger('afterClose', obj); 30425 30426 if (!F.coming && !F.current) { 30427 if (obj.overlay) { 30428 F.overlay.close( obj.overlay, cleanup ); 30429 } else { 30430 cleanup(); 30431 } 30432 } 30433 }, 30434 30435 _translate : function( str ) { 30436 var obj = F.coming || F.current, 30437 arr = obj.locales[ obj.locale ]; 30438 30439 return str.replace(/\{\{(\w+)\}\}/g, function(match, n) { 30440 var value = arr[n]; 30441 30442 if (value === undefined) { 30443 return match; 30444 } 30445 30446 return value; 30447 }); 30448 } 30449 }); 30450 30451 /* 30452 * Transition object 30453 */ 30454 30455 F.transitions = { 30456 _getOrig: function( object ) { 30457 var obj = object || F.current, 30458 wrap = obj.wrap, 30459 element = obj.element, 30460 orig = obj.orig, 30461 view = F.getViewport(), 30462 pos = {}, 30463 width = 50, 30464 height = 50; 30465 30466 if (!orig && element && element.is(':visible')) { 30467 orig = element.find('img:first:visible'); 30468 30469 if (!orig.length) { 30470 orig = element; 30471 } 30472 } 30473 30474 // If there is no orig element, maybe only the first thumbnail is visible 30475 if (!orig && obj.group[0].element) { 30476 orig = obj.group[0].element.find('img:visible:first'); 30477 } 30478 30479 if (isQuery(orig) && orig.is(':visible')) { 30480 pos = orig.offset(); 30481 30482 if (orig.is('img')) { 30483 width = orig.outerWidth(); 30484 height = orig.outerHeight(); 30485 } 30486 30487 if (F.lock) { 30488 pos.top -= W.scrollTop(); 30489 pos.left -= W.scrollLeft(); 30490 } 30491 30492 } else { 30493 pos.top = view.y + (view.h - height) * obj.topRatio; 30494 pos.left = view.x + (view.w - width) * obj.leftRatio; 30495 } 30496 30497 pos = { 30498 top : getValue( pos.top - (wrap.outerHeight(true) - wrap.height() ) * 0.5 ), 30499 left : getValue( pos.left - (wrap.outerWidth(true) - wrap.width() ) * 0.5 ), 30500 width : getValue( width ), 30501 height : getValue( height ) 30502 }; 30503 30504 return pos; 30505 }, 30506 30507 _getCenter: function( object ) { 30508 var obj = object || F.current, 30509 wrap = obj.wrap, 30510 view = F.getViewport(), 30511 pos = {}, 30512 top = view.y, 30513 left = view.x; 30514 30515 pos = { 30516 top : getValue( Math.max(top, top + ((view.h - wrap.outerHeight(true)) * obj.topRatio)) ), 30517 left : getValue( Math.max(left, left + ((view.w - wrap.outerWidth(true)) * obj.leftRatio)) ), 30518 width : getValue( wrap.width() ), 30519 height : getValue( wrap.height() ) 30520 }; 30521 30522 return pos; 30523 }, 30524 30525 _prepare : function( object, forClosing ) { 30526 var obj = object || F.current, 30527 wrap = obj.wrap, 30528 inner = obj.inner; 30529 30530 // Little trick to avoid animating both elements and to improve performance 30531 wrap.height( wrap.height() ); 30532 30533 inner.css({ 30534 'width' : (inner.width() * 100 / wrap.width() ) + '%', 30535 'height' : (Math.floor( (inner.height() * 100 / wrap.height() ) * 100 ) / 100 ) + '%' 30536 }); 30537 30538 if (forClosing === true) { 30539 wrap.find('.fancybox-title, .fancybox-spacer, .fancybox-close, .fancybox-nav').remove(); 30540 } 30541 30542 inner.css('overflow', 'hidden'); 30543 }, 30544 30545 fade : function( object, stage ) { 30546 var pos = this._getCenter( object ), 30547 opa = {opacity: 0}; 30548 30549 return ((stage === 'open' || stage === 'changeIn') ? [ $.extend(pos, opa), {opacity: 1} ] : [ {}, opa ]); 30550 }, 30551 30552 drop : function( object, stage ) { 30553 var a = $.extend(this._getCenter( object ), {opacity: 1}), 30554 b = $.extend({}, a, {opacity: 0, top: getValue( Math.max( F.getViewport().y - object.margin[0], getScalar( a.top ) - 200 ) )}); 30555 30556 return ((stage === 'open' || stage === 'changeIn') ? [ b, a ] : [ {}, b ]); 30557 }, 30558 30559 elastic : function( object, stage ) { 30560 var wrap = object.wrap, 30561 margin = object.margin, 30562 view = F.getViewport(), 30563 direction = F.direction, 30564 pos = this._getCenter( object ), 30565 from = $.extend({}, pos), 30566 to = $.extend({}, pos), 30567 prop, 30568 amount, 30569 value; 30570 30571 if (stage === 'open') { 30572 from = this._getOrig( object ); 30573 30574 } else if (stage === 'close') { 30575 from = {}; 30576 to = this._getOrig( object ); 30577 30578 } else if (direction) { 30579 // Calculate max distance and try to avoid scrollbars 30580 prop = (direction === 'up' || direction === 'down') ? 'top' : 'left'; 30581 amount = (direction === 'up' || direction === 'left') ? 200 : -200; 30582 30583 if (stage === 'changeIn') { 30584 value = getScalar(from[ prop ]) + amount; 30585 30586 if (direction === 'left') { 30587 // from viewport right to center 30588 value = Math.min( value, view.x + view.w - margin[3] - wrap.outerWidth() - 1 ); 30589 30590 } else if (direction === 'right') { 30591 // from viewport left to center 30592 value = Math.max( value, view.x - margin[1] ); 30593 30594 } else if (direction === 'up') { 30595 // from viewport bottom to center 30596 value = Math.min( value, view.y + view.h - margin[0] - wrap.outerHeight() - 1); 30597 30598 } else { 30599 // down - from viewport top to center 30600 value = Math.max( value, view.y - margin[2] ); 30601 } 30602 30603 from[ prop ] = value; 30604 30605 } else { 30606 value = getScalar(wrap.css(prop)) - amount; 30607 from = {}; 30608 30609 if (direction === 'left') { 30610 // from viewport center to left 30611 value = Math.max( value, view.x - margin[3] ); 30612 30613 } else if (direction === 'right') { 30614 // from viewport center to right 30615 value = Math.min( value, view.x + view.w - margin[1] - wrap.outerWidth() - 1 ); 30616 30617 } else if (direction === 'up') { 30618 // from viewport center to top 30619 value = Math.max( value, view.y - margin[0] ); 30620 30621 } else { 30622 // down - from center to bottom 30623 value = Math.min( value, view.y + view.h - margin[2] - wrap.outerHeight() - 1 ); 30624 } 30625 30626 to[ prop ] = value; 30627 } 30628 } 30629 30630 if (stage === 'open' || stage === 'changeIn') { 30631 from.opacity = 0; 30632 to.opacity = 1; 30633 30634 } else { 30635 to.opacity = 0; 30636 } 30637 30638 return [ from, to ]; 30639 }, 30640 30641 open : function() { 30642 var current = F.current, 30643 previous = F.previous, 30644 direction = F.direction, 30645 effect, 30646 pos, 30647 speed, 30648 easing, 30649 stage; 30650 30651 if (previous) { 30652 previous.wrap.stop(true).removeClass('fancybox-opened'); 30653 } 30654 30655 if ( F.isOpened ) { 30656 effect = current.nextEffect, 30657 speed = current.nextSpeed; 30658 easing = current.nextEasing; 30659 stage = 'changeIn'; 30660 30661 } else { 30662 effect = current.openEffect; 30663 speed = current.openSpeed; 30664 easing = current.openEasing; 30665 stage = 'open'; 30666 } 30667 30668 /* 30669 * Open current item 30670 */ 30671 30672 if (effect === 'none') { 30673 F._afterZoomIn(); 30674 30675 } else { 30676 pos = this[ effect ]( current, stage ); 30677 30678 if (effect === 'elastic') { 30679 this._prepare( current ); 30680 } 30681 30682 current.wrap.css( pos[ 0 ] ); 30683 30684 current.wrap.animate( 30685 pos[ 1 ], 30686 speed, 30687 easing, 30688 F._afterZoomIn 30689 ); 30690 } 30691 30692 /* 30693 * Close previous item 30694 */ 30695 if (previous) { 30696 if (!F.isOpened || previous.prevEffect === 'none') { 30697 // Remove previous item if it has not fully opened 30698 removeWrap( $('.fancybox-wrap').not( current.wrap ) ); 30699 30700 } else { 30701 previous.wrap.stop(true).removeClass('fancybox-opened'); 30702 30703 pos = this[ previous.prevEffect ]( previous, 'changeOut' ); 30704 30705 this._prepare( previous, true ); 30706 30707 previous.wrap.animate( 30708 pos[ 1 ], 30709 previous.prevSpeed, 30710 previous.prevEasing, 30711 function() { 30712 removeWrap( previous.wrap ); 30713 } 30714 ); 30715 } 30716 } 30717 }, 30718 30719 close : function() { 30720 var current = F.current, 30721 wrap = current.wrap.stop(true).removeClass('fancybox-opened'), 30722 effect = current.closeEffect, 30723 pos; 30724 30725 if (effect === 'none') { 30726 return F._afterZoomOut(); 30727 } 30728 30729 this._prepare( current, true ); 30730 30731 pos = this[ effect ]( current, 'close' ); 30732 30733 wrap.addClass('fancybox-animating') 30734 .animate( 30735 pos[ 1 ], 30736 current.closeSpeed, 30737 current.closeEasing, 30738 F._afterZoomOut 30739 ); 30740 } 30741 }; 30742 30743 /* 30744 * Slideshow object 30745 */ 30746 30747 F.slideshow = { 30748 _clear : function () { 30749 if (this._timer) { 30750 clearTimeout(this._timer); 30751 } 30752 }, 30753 _set : function () { 30754 this._clear(); 30755 30756 if (F.current && this.isActive) { 30757 this._timer = setTimeout(F.next, this._speed); 30758 } 30759 }, 30760 30761 _timer : null, 30762 _speed : null, 30763 30764 isActive : false, 30765 30766 start : function ( speed ) { 30767 var current = F.current; 30768 30769 if (current && (current.loop || current.index < current.group.length - 1)) { 30770 this.stop(); 30771 30772 this.isActive = true; 30773 this._speed = speed || current.playSpeed; 30774 30775 D.bind({ 30776 'beforeLoad.player' : $.proxy(this._clear, this), 30777 'onUpdate.player' : $.proxy(this._set, this), 30778 'onCancel.player beforeClose.player' : $.proxy(this.stop, this) 30779 }); 30780 30781 this._set(); 30782 30783 F.trigger('onPlayStart'); 30784 } 30785 }, 30786 30787 stop : function () { 30788 this._clear(); 30789 30790 D.unbind('.player'); 30791 30792 this.isActive = false; 30793 this._timer = this._speed = null; 30794 30795 F.trigger('onPlayEnd'); 30796 }, 30797 30798 toggle : function() { 30799 if (this.isActive) { 30800 this.stop(); 30801 30802 } else { 30803 this.start.apply(this, arguments ); 30804 } 30805 } 30806 }; 30807 30808 /* 30809 * Overlay object 30810 */ 30811
30812 F.overlay = { 30813 el : null, // current handle 30814 theme : '', // current theme 30815 30816 // Public methods 30817 open : function(opts) { 30818 var that = this, 30819 el = this.el, 30820 fixed = F.defaults.fixed, 30821 opacity, 30822 theme; 30823 30824 opts = $.extend({}, F.defaults.overlay, opts); 30825 30826 if (el) { 30827 el.stop(true).removeAttr('style').unbind('.overlay'); 30828 30829 } else { 30830 el = $('<div class="fancybox-overlay' + (fixed ? ' fancybox-overlay-fixed' : '' ) + '"></div>').appendTo( opts.parent || 'body' ); 30831 } 30832 30833 if (opts.closeClick) { 30834 el.bind('click.overlay', function(e) { 30835 // fix Android touch event bubbling issue 30836 if (F.lastTouch && (getTime() - F.lastTouch) < 300) { 30837 return false; 30838 } 30839 30840 if (F.isActive) { 30841 F.close(); 30842 } else { 30843 that.close(); 30844 } 30845 30846 return false; 30847 }); 30848 } 30849 30850 theme = opts.theme || (F.coming ? F.coming.theme : 'default'); 30851 30852 if (theme !== this.theme) { 30853 el.removeClass('fancybox-' + this.theme + '-overlay') 30854 } 30855 30856 this.theme = theme; 30857 30858 el.addClass('fancybox-' + theme + '-overlay').css( opts.css ); 30859 30860 opacity = el.css('opacity'); 30861 30862 if (!this.el && opacity < 1 && opts.speedIn) { 30863 el.css({ 30864 opacity : 0, 30865 filter : 'alpha(opacity=0)' // This fixes IE flickering 30866 }) 30867 .fadeTo( opts.speedIn, opacity ); 30868 } 30869 30870 this.el = el; 30871 30872 if (!fixed) { 30873 W.bind('resize.overlay', $.proxy( this.update, this) ); 30874 30875 this.update(); 30876 } 30877 }, 30878 30879 close : function(opts, callback) { 30880 opts = $.extend({}, F.defaults.overlay, opts); 30881 30882 if (this.el) { 30883 this.el.stop(true).fadeOut(opts.speedOut, function() { 30884 W.unbind('resize.overlay'); 30885 30886 $('.fancybox-overlay').remove(); 30887 30888 F.overlay.el = null; 30889 30890 if ($.isFunction(callback)) { 30891 callback(); 30892 } 30893 }); 30894 } 30895 }, 30896 30897 update : function () { 30898 // Reset width/height so it will not mess 30899 this.el.css({width: '100%', height: '100%'}); 30900 30901 this.el.width(D.width()).height(D.height()); 30902 } 30903 }; 30904 30905 /* 30906 * Touch object - adds swipe left/right events 30907 */ 30908 30909 F.touch = { 30910 startX : 0, 30911 wrapX : 0, 30912 dx : 0, 30913 isMoving : false, 30914 30915 _start : function(e) { 30916 var current = F.current, 30917 data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, 30918 now = getTime(); 30919 30920 if (!F.isOpen || F.wrap.is(':animated') || !( $(e.target).is(F.inner) || $(e.target).parent().is(F.inner) )) { 30921 return; 30922 } 30923 30924 if (F.lastTouch && (now - F.lastTouch) < 300) { 30925 e.preventDefault(); 30926 30927 F.lastTouch = now; 30928 30929 this._cancel(true); 30930 30931 F.toggle(); 30932 30933 return false; 30934 } 30935 30936 F.lastTouch = now; 30937 30938 if (F.wrap && F.wrap.outerWidth() > F.getViewport().w) { 30939 return; 30940 } 30941 30942 e.preventDefault(); 30943 30944 if (data && F.wrap && F.wrap.outerWidth() < F.getViewport().w) { 30945 this.startX = data.pageX; 30946 this.wrapX = F.wrap.position().left; 30947 this.isMoving = true; 30948 30949 F.inner 30950 .bind('touchmove.fb', $.proxy(this._move, this) ) 30951 .one("touchend.fb touchcancel.fb", $.proxy(this._cancel, this) ); 30952 } 30953 }, 30954 30955 _move : function(e) { 30956 var data = e.originalEvent.touches ? e.originalEvent.touches[0] : e, 30957 dx = this.startX - data.pageX; 30958 30959 if (!this.isMoving || !F.isOpen) { 30960 return; 30961 } 30962 30963 this.dx = dx; 30964 30965 if (F.current.wrap.outerWidth(true) <= W.width()) { 30966 30967 if (Math.abs(dx) >= 50) { 30968 e.preventDefault(); 30969 30970 this.last = 0; 30971 30972 this._cancel(true); 30973 30974 if (dx > 0) { 30975 F.next('left'); 30976 30977 } else { 30978 F.prev('right'); 30979 } 30980 30981 } else if (Math.abs(dx) > 3) { 30982 e.preventDefault(); 30983 30984 this.last = 0; 30985 30986 F.wrap.css('left', this.wrapX - dx); 30987 } 30988 } 30989 }, 30990 30991 _clear : function() { 30992 this.startX = this.wrapX = this.dx = 0; 30993 this.isMoving = false; 30994 }, 30995 30996 _cancel : function( stop ) { 30997 if (F.inner) { 30998 F.inner.unbind('touchmove.fb'); 30999 } 31000 31001 if (F.isOpen && Math.abs(this.dx) > 3) { 31002 F.reposition(false); 31003 } 31004 31005 this._clear(); 31006 }, 31007 31008 init : function() { 31009 var that = this; 31010 31011 if (F.inner && F.touch) { 31012 this._cancel(true); 31013 31014 F.inner.bind('touchstart.fb', $.proxy(this._start, this)); 31015 } 31016 } 31017 }; 31018 31019 /* 31020 * Add default easing 31021 */ 31022 31023 if (!$.easing.easeOutQuad) { 31024 $.easing.easeOutQuad = function (x, t, b, c, d) { 31025 return -c *(t/=d)*(t-2) + b; 31026 }; 31027 } 31028 31029 /* 31030 * 31031 */ 31032 31033 D.ready(function() { 31034 var w1, w2, scrollV, scrollH; 31035 31036 // Tests that need a body at doc ready 31037 if ( $.scrollbarWidth === undefined ) { 31038 $.scrollbarWidth = function() { 31039 var parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body'), 31040 child = parent.children(), 31041 width = child.innerWidth() - child.height( 99 ).innerWidth(); 31042 31043 parent.remove(); 31044 31045 return width; 31046 }; 31047 } 31048 31049 if ( $.support.fixedPosition === undefined ) { 31050 $.support.fixedPosition = (function() { 31051 var elem = $('<div style="position:fixed;top:20px;padding:0;margin:0;border:0;"></div>').appendTo('body'), 31052 fixed = elem.css( 'position' ) === 'fixed' && ((elem[0].offsetTop > 18 && elem[0].offsetTop < 22) || elem[0].offsetTop === 15 ); 31053 31054 elem.remove(); 31055 31056 return fixed; 31057 }()); 31058 } 31059 31060 $.extend(F.defaults, { 31061 scrollbarWidth : $.scrollbarWidth(), 31062 fixed : $.support.fixedPosition, 31063 parent : $('body') 31064 }); 31065 31066 // Quick and dirty code to get page scroll-bar width and create CSS style 31067 // Workaround for FF jumping bug 31068 scrollV = W.scrollTop(); 31069 scrollH = W.scrollLeft(); 31070 31071 w1 = $(window).width(); 31072 31073 H.addClass('fancybox-lock-test'); 31074 31075 w2 = $(window).width(); 31076 31077 H.removeClass('fancybox-lock-test'); 31078 31079 W.scrollTop( scrollV ).scrollLeft( scrollH ); 31080 31081 F.lockMargin = (w2 - w1); 31082 31083 $("<style type='text/css'>.fancybox-margin{margin-right:" + F.lockMargin + "px;}</style>").appendTo("head"); 31084 31085 // Auto start 31086 if ($("script[src*='magic.js']").attr('src').match(/autorun/)) { 31087 $("a[href$='.jpg'],a[href$='.png'],a[href$='.gif'],.fancybox").attr('data-fancybox-group', 'gallery').fancybox(); 31088 } 31089 }); 31090 31091 // jQuery plugin initialization 31092 $.fn.fancybox = function (options) { 31093 var that = this, 31094 selector = this.length ? this.selector : false, 31095 live = (selector && selector.indexOf('()') < 0 && !(options && options.live === false)); 31096 31097 var handler = function(e) { 31098 var collection = live ? $(selector) : that, 31099 group = $(this).blur(), 31100 param = options.groupAttr || 'data-fancybox-group', 31101 value = group.attr( param ), 31102 tmp = this.rel; 31103 31104 if (!value && tmp && tmp !== 'nofollow') { 31105 param = 'rel'; 31106 value = tmp; 31107 } 31108 31109 if (value) { 31110 group = collection.filter('[' + param + '="' + value + '"]'); 31111 31112 options.index = group.index( this ); 31113 } 31114 31115 if (group.length) { 31116 e.preventDefault(); 31117 31118 F.open(group.get(), options); 31119 } 31120 }; 31121 31122 options = options || {}; 31123 31124 if (live) { 31125 D.undelegate(selector, 'click.fb-start').delegate(selector + ":not('.fancybox-close,.fancybox-nav,.fancybox-wrap')", 'click.fb-start', handler); 31126 31127 } else { 31128 that.unbind('click.fb-start').bind('click.fb-start', handler); 31129 } 31130 31131 return this; 31132 }; 31133 31134}(window, document, jQuery)); 31135/* 31136 _ _ _ _ 31137 ___| (_) ___| | __ (_)___ 31138/ __| | |/ __| |/ / | / __| 31139\__ \ | | (__| < _ | \__ \ 31140|___/_|_|\___|_|\_(_)/ |___/ 31141 |__/ 31142 31143 Version: 1.5.8 31144 Author: Ken Wheeler 31145 Website: http://kenwheeler.github.io 31146 Docs: http://kenwheeler.github.io/slick 31147 Repo: http://github.com/kenwheeler/slick 31148 Issues: http://github.com/kenwheeler/slick/issues 31149 31150 */ 31151/* global window, document, define, jQuery, setInterval, clearInterval */ 31152(function(factory) { 31153 'use strict'; 31154 if (typeof define === 'function' && define.amd) { 31155 define(['jquery'], factory); 31156 } else if (typeof exports !== 'undefined') { 31157 module.exports = factory(require('jquery')); 31158 } else { 31159 factory(jQuery); 31160 } 31161 31162}(function($) { 31163 'use strict'; 31164 var Slick = window.Slick || {}; 31165 31166 Slick = (function() { 31167 31168 var instanceUid = 0; 31169 31170 function Slick(element, settings) { 31171 31172 var _ = this, dataSettings; 31173 31174 _.defaults = { 31175 accessibility: true, 31176 adaptiveHeight: false, 31177 appendArrows: $(element), 31178 appendDots: $(element), 31179 arrows: true, 31180 asNavFor: null, 31181 prevArrow: '<button type="button" data-role="none" class="slick-prev" aria-label="Previous" tabindex="0" role="button">Previous</button>', 31182 nextArrow: '<button type="button" data-role="none" class="slick-next" aria-label="Next" tabindex="0" role="button">Next</button>', 31183 autoplay: false, 31184 autoplaySpeed: 3000, 31185 centerMode: false, 31186 centerPadding: '50px', 31187 cssEase: 'ease', 31188 customPaging: function(slider, i) { 31189 return '<button type="button" data-role="none" role="button" aria-required="false" tabindex="0">' + (i + 1) + '</button>'; 31190 }, 31191 dots: false, 31192 dotsClass: 'slick-dots', 31193 draggable: true, 31194 easing: 'linear', 31195 edgeFriction: 0.35, 31196 fade: false, 31197 focusOnSelect: false, 31198 infinite: true, 31199 initialSlide: 0, 31200 lazyLoad: 'ondemand', 31201 mobileFirst: false, 31202 pauseOnHover: true, 31203 pauseOnDotsHover: false, 31204 respondTo: 'window', 31205 responsive: null, 31206 rows: 1, 31207 rtl: false, 31208 slide: '', 31209 slidesPerRow: 1, 31210 slidesToShow: 1, 31211 slidesToScroll: 1, 31212 speed: 500, 31213 swipe: true, 31214 swipeToSlide: false, 31215 touchMove: true, 31216 touchThreshold: 5, 31217 useCSS: true, 31218 useTransform: false, 31219 variableWidth: false, 31220 vertical: false, 31221 verticalSwiping: false, 31222 waitForAnimate: true, 31223 zIndex: 1000 31224 }; 31225 31226 _.initials = { 31227 animating: false, 31228 dragging: false, 31229 autoPlayTimer: null, 31230 currentDirection: 0, 31231 currentLeft: null, 31232 currentSlide: 0, 31233 direction: 1, 31234 $dots: null, 31235 listWidth: null, 31236 listHeight: null, 31237 loadIndex: 0, 31238 $nextArrow: null, 31239 $prevArrow: null, 31240 slideCount: null, 31241 slideWidth: null, 31242 $slideTrack: null, 31243 $slides: null, 31244 sliding: false, 31245 slideOffset: 0, 31246 swipeLeft: null, 31247 $list: null, 31248 touchObject: {}, 31249 transformsEnabled: false,
vendor: 6,056 bytes, lines 31250-31430
31250 unslicked: false 31251 }; 31252 31253 $.extend(_, _.initials); 31254 31255 _.activeBreakpoint = null; 31256 _.animType = null; 31257 _.animProp = null; 31258 _.breakpoints = []; 31259 _.breakpointSettings = []; 31260 _.cssTransitions = false; 31261 _.hidden = 'hidden'; 31262 _.paused = false; 31263 _.positionProp = null; 31264 _.respondTo = null; 31265 _.rowCount = 1; 31266 _.shouldClick = true; 31267 _.$slider = $(element); 31268 _.$slidesCache = null; 31269 _.transformType = null; 31270 _.transitionType = null; 31271 _.visibilityChange = 'visibilitychange'; 31272 _.windowWidth = 0; 31273 _.windowTimer = null; 31274 31275 dataSettings = $(element).data('slick') || {}; 31276 31277 _.options = $.extend({}, _.defaults, dataSettings, settings); 31278 31279 _.currentSlide = _.options.initialSlide; 31280 31281 _.originalSettings = _.options; 31282 31283 if (typeof document.mozHidden !== 'undefined') { 31284 _.hidden = 'mozHidden'; 31285 _.visibilityChange = 'mozvisibilitychange'; 31286 } else if (typeof document.webkitHidden !== 'undefined') { 31287 _.hidden = 'webkitHidden'; 31288 _.visibilityChange = 'webkitvisibilitychange'; 31289 } 31290 31291 _.autoPlay = $.proxy(_.autoPlay, _); 31292 _.autoPlayClear = $.proxy(_.autoPlayClear, _); 31293 _.changeSlide = $.proxy(_.changeSlide, _); 31294 _.clickHandler = $.proxy(_.clickHandler, _); 31295 _.selectHandler = $.proxy(_.selectHandler, _); 31296 _.setPosition = $.proxy(_.setPosition, _); 31297 _.swipeHandler = $.proxy(_.swipeHandler, _); 31298 _.dragHandler = $.proxy(_.dragHandler, _); 31299 _.keyHandler = $.proxy(_.keyHandler, _); 31300 _.autoPlayIterator = $.proxy(_.autoPlayIterator, _); 31301 31302 _.instanceUid = instanceUid++; 31303 31304 // A simple way to check for HTML strings 31305 // Strict HTML recognition (must start with <) 31306 // Extracted from jQuery v1.11 source 31307 _.htmlExpr = /^(?:\s*(<[\w\W]+>)[^>]*)$/; 31308 31309 31310 _.registerBreakpoints(); 31311 _.init(true); 31312 _.checkResponsive(true); 31313 31314 } 31315 31316 return Slick; 31317 31318 }()); 31319 31320 Slick.prototype.addSlide = Slick.prototype.slickAdd = function(markup, index, addBefore) { 31321 31322 var _ = this; 31323 31324 if (typeof(index) === 'boolean') { 31325 addBefore = index; 31326 index = null; 31327 } else if (index < 0 || (index >= _.slideCount)) { 31328 return false; 31329 } 31330 31331 _.unload(); 31332 31333 if (typeof(index) === 'number') { 31334 if (index === 0 && _.$slides.length === 0) { 31335 $(markup).appendTo(_.$slideTrack); 31336 } else if (addBefore) { 31337 $(markup).insertBefore(_.$slides.eq(index)); 31338 } else { 31339 $(markup).insertAfter(_.$slides.eq(index)); 31340 } 31341 } else { 31342 if (addBefore === true) { 31343 $(markup).prependTo(_.$slideTrack); 31344 } else { 31345 $(markup).appendTo(_.$slideTrack); 31346 } 31347 } 31348 31349 _.$slides = _.$slideTrack.children(this.options.slide); 31350 31351 _.$slideTrack.children(this.options.slide).detach(); 31352 31353 _.$slideTrack.append(_.$slides); 31354 31355 _.$slides.each(function(index, element) { 31356 $(element).attr('data-slick-index', index); 31357 }); 31358 31359 _.$slidesCache = _.$slides; 31360 31361 _.reinit(); 31362 31363 }; 31364 31365 Slick.prototype.animateHeight = function() { 31366 var _ = this; 31367 if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) { 31368 var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true); 31369 _.$list.animate({ 31370 height: targetHeight 31371 }, _.options.speed); 31372 } 31373 }; 31374 31375 Slick.prototype.animateSlide = function(targetLeft, callback) { 31376 31377 var animProps = {}, 31378 _ = this; 31379 31380 _.animateHeight(); 31381 31382 if (_.options.rtl === true && _.options.vertical === false) { 31383 targetLeft = -targetLeft; 31384 } 31385 if (_.transformsEnabled === false) { 31386 if (_.options.vertical === false) { 31387 _.$slideTrack.animate({ 31388 left: targetLeft 31389 }, _.options.speed, _.options.easing, callback); 31390 } else { 31391 _.$slideTrack.animate({ 31392 top: targetLeft 31393 }, _.options.speed, _.options.easing, callback); 31394 } 31395 31396 } else { 31397 31398 if (_.cssTransitions === false) { 31399 if (_.options.rtl === true) { 31400 _.currentLeft = -(_.currentLeft); 31401 } 31402 $({ 31403 animStart: _.currentLeft 31404 }).animate({ 31405 animStart: targetLeft 31406 }, { 31407 duration: _.options.speed, 31408 easing: _.options.easing, 31409 step: function(now) { 31410 now = Math.ceil(now); 31411 if (_.options.vertical === false) { 31412 animProps[_.animType] = 'translate(' + 31413 now + 'px, 0px)'; 31414 _.$slideTrack.css(animProps); 31415 } else { 31416 animProps[_.animType] = 'translate(0px,' + 31417 now + 'px)'; 31418 _.$slideTrack.css(animProps); 31419 } 31420 }, 31421 complete: function() { 31422 if (callback) { 31423 callback.call(); 31424 } 31425 } 31426 }); 31427 31428 } else { 31429 31430 _.applyTransition();
31431 targetLeft = Math.ceil(targetLeft); 31432 31433 if (_.options.vertical === false) { 31434 animProps[_.animType] = 'translate3d(' + targetLeft + 'px, 0px, 0px)'; 31435 } else { 31436 animProps[_.animType] = 'translate3d(0px,' + targetLeft + 'px, 0px)'; 31437 } 31438 _.$slideTrack.css(animProps); 31439 31440 if (callback) { 31441 setTimeout(function() { 31442 31443 _.disableTransition(); 31444 31445 callback.call(); 31446 }, _.options.speed); 31447 } 31448 31449 } 31450 31451 } 31452 31453 }; 31454 31455 Slick.prototype.asNavFor = function(index) { 31456 31457 var _ = this, 31458 asNavFor = _.options.asNavFor; 31459 31460 if ( asNavFor && asNavFor !== null ) { 31461 asNavFor = $(asNavFor).not(_.$slider); 31462 } 31463 31464 if ( asNavFor !== null && typeof asNavFor === 'object' ) { 31465 asNavFor.each(function() { 31466 var target = $(this).slick('getSlick'); 31467 if(!target.unslicked) { 31468 target.slideHandler(index, true); 31469 } 31470 }); 31471 } 31472 31473 }; 31474 31475 Slick.prototype.applyTransition = function(slide) { 31476 31477 var _ = this, 31478 transition = {}; 31479 31480 if (_.options.fade === false) { 31481 transition[_.transitionType] = _.transformType + ' ' + _.options.speed + 'ms ' + _.options.cssEase; 31482 } else { 31483 transition[_.transitionType] = 'opacity ' + _.options.speed + 'ms ' + _.options.cssEase; 31484 } 31485 31486 if (_.options.fade === false) { 31487 _.$slideTrack.css(transition); 31488 } else { 31489 _.$slides.eq(slide).css(transition); 31490 } 31491 31492 }; 31493 31494 Slick.prototype.autoPlay = function() { 31495 31496 var _ = this; 31497 31498 if (_.autoPlayTimer) { 31499 clearInterval(_.autoPlayTimer); 31500 } 31501 31502 if (_.slideCount > _.options.slidesToShow && _.paused !== true) { 31503 _.autoPlayTimer = setInterval(_.autoPlayIterator, 31504 _.options.autoplaySpeed); 31505 } 31506 31507 }; 31508 31509 Slick.prototype.autoPlayClear = function() { 31510 31511 var _ = this; 31512 if (_.autoPlayTimer) { 31513 clearInterval(_.autoPlayTimer); 31514 } 31515 31516 }; 31517 31518 Slick.prototype.autoPlayIterator = function() { 31519 31520 var _ = this; 31521 31522 if (_.options.infinite === false) { 31523 31524 if (_.direction === 1) { 31525 31526 if ((_.currentSlide + 1) === _.slideCount - 31527 1) { 31528 _.direction = 0; 31529 } 31530 31531 _.slideHandler(_.currentSlide + _.options.slidesToScroll); 31532 31533 } else { 31534 31535 if ((_.currentSlide - 1 === 0)) { 31536 31537 _.direction = 1; 31538 31539 } 31540 31541 _.slideHandler(_.currentSlide - _.options.slidesToScroll); 31542 31543 } 31544 31545 } else { 31546 31547 _.slideHandler(_.currentSlide + _.options.slidesToScroll); 31548 31549 } 31550 31551 }; 31552 31553 Slick.prototype.buildArrows = function() { 31554 31555 var _ = this; 31556 31557 if (_.options.arrows === true ) { 31558 31559 _.$prevArrow = $(_.options.prevArrow).addClass('slick-arrow'); 31560 _.$nextArrow = $(_.options.nextArrow).addClass('slick-arrow'); 31561 31562 if( _.slideCount > _.options.slidesToShow ) { 31563 31564 _.$prevArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex'); 31565 _.$nextArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex'); 31566 31567 if (_.htmlExpr.test(_.options.prevArrow)) { 31568 _.$prevArrow.prependTo(_.options.appendArrows); 31569 } 31570 31571 if (_.htmlExpr.test(_.options.nextArrow)) { 31572 _.$nextArrow.appendTo(_.options.appendArrows); 31573 } 31574 31575 if (_.options.infinite !== true) { 31576 _.$prevArrow 31577 .addClass('slick-disabled') 31578 .attr('aria-disabled', 'true'); 31579 } 31580 31581 } else { 31582 31583 _.$prevArrow.add( _.$nextArrow ) 31584 31585 .addClass('slick-hidden') 31586 .attr({ 31587 'aria-disabled': 'true', 31588 'tabindex': '-1' 31589 }); 31590 31591 } 31592 31593 } 31594 31595 }; 31596 31597 Slick.prototype.buildDots = function() { 31598 31599 var _ = this, 31600 i, dotString; 31601 31602 if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { 31603 31604 dotString = '<ul class="' + _.options.dotsClass + '">'; 31605 31606 for (i = 0; i <= _.getDotCount(); i += 1) { 31607 dotString += '<li>' + _.options.customPaging.call(this, _, i) + '</li>'; 31608 } 31609 31610 dotString += '</ul>'; 31611 31612 _.$dots = $(dotString).appendTo(
vendor: 9,145 bytes, lines 31613-31865
31613 _.options.appendDots); 31614 31615 _.$dots.find('li').first().addClass('slick-active').attr('aria-hidden', 'false'); 31616 31617 } 31618 31619 }; 31620 31621 Slick.prototype.buildOut = function() { 31622 31623 var _ = this; 31624 31625 _.$slides = 31626 _.$slider 31627 .children( _.options.slide + ':not(.slick-cloned)') 31628 .addClass('slick-slide'); 31629 31630 _.slideCount = _.$slides.length; 31631 31632 _.$slides.each(function(index, element) { 31633 $(element) 31634 .attr('data-slick-index', index) 31635 .data('originalStyling', $(element).attr('style') || ''); 31636 }); 31637 31638 _.$slidesCache = _.$slides; 31639 31640 _.$slider.addClass('slick-slider'); 31641 31642 _.$slideTrack = (_.slideCount === 0) ? 31643 $('<div class="slick-track"/>').appendTo(_.$slider) : 31644 _.$slides.wrapAll('<div class="slick-track"/>').parent(); 31645 31646 _.$list = _.$slideTrack.wrap( 31647 '<div aria-live="polite" class="slick-list"/>').parent(); 31648 _.$slideTrack.css('opacity', 0); 31649 31650 if (_.options.centerMode === true || _.options.swipeToSlide === true) { 31651 _.options.slidesToScroll = 1; 31652 } 31653 31654 $('img[data-lazy]', _.$slider).not('[src]').addClass('slick-loading'); 31655 31656 _.setupInfinite(); 31657 31658 _.buildArrows(); 31659 31660 _.buildDots(); 31661 31662 _.updateDots(); 31663 31664 31665 _.setSlideClasses(typeof _.currentSlide === 'number' ? _.currentSlide : 0); 31666 31667 if (_.options.draggable === true) { 31668 _.$list.addClass('draggable'); 31669 } 31670 31671 }; 31672 31673 Slick.prototype.buildRows = function() { 31674 31675 var _ = this, a, b, c, newSlides, numOfSlides, originalSlides,slidesPerSection; 31676 31677 newSlides = document.createDocumentFragment(); 31678 originalSlides = _.$slider.children(); 31679 31680 if(_.options.rows > 1) { 31681 31682 slidesPerSection = _.options.slidesPerRow * _.options.rows; 31683 numOfSlides = Math.ceil( 31684 originalSlides.length / slidesPerSection 31685 ); 31686 31687 for(a = 0; a < numOfSlides; a++){ 31688 var slide = document.createElement('div'); 31689 for(b = 0; b < _.options.rows; b++) { 31690 var row = document.createElement('div'); 31691 for(c = 0; c < _.options.slidesPerRow; c++) { 31692 var target = (a * slidesPerSection + ((b * _.options.slidesPerRow) + c)); 31693 if (originalSlides.get(target)) { 31694 row.appendChild(originalSlides.get(target)); 31695 } 31696 } 31697 slide.appendChild(row); 31698 } 31699 newSlides.appendChild(slide); 31700 } 31701 31702 _.$slider.html(newSlides); 31703 _.$slider.children().children().children() 31704 .css({ 31705 'width':(100 / _.options.slidesPerRow) + '%', 31706 'display': 'inline-block' 31707 }); 31708 31709 } 31710 31711 }; 31712 31713 Slick.prototype.checkResponsive = function(initial, forceUpdate) { 31714 31715 var _ = this, 31716 breakpoint, targetBreakpoint, respondToWidth, triggerBreakpoint = false; 31717 var sliderWidth = _.$slider.width(); 31718 var windowWidth = window.innerWidth || $(window).width(); 31719 31720 if (_.respondTo === 'window') { 31721 respondToWidth = windowWidth; 31722 } else if (_.respondTo === 'slider') { 31723 respondToWidth = sliderWidth; 31724 } else if (_.respondTo === 'min') { 31725 respondToWidth = Math.min(windowWidth, sliderWidth); 31726 } 31727 31728 if ( _.options.responsive && 31729 _.options.responsive.length && 31730 _.options.responsive !== null) { 31731 31732 targetBreakpoint = null; 31733 31734 for (breakpoint in _.breakpoints) { 31735 if (_.breakpoints.hasOwnProperty(breakpoint)) { 31736 if (_.originalSettings.mobileFirst === false) { 31737 if (respondToWidth < _.breakpoints[breakpoint]) { 31738 targetBreakpoint = _.breakpoints[breakpoint]; 31739 } 31740 } else { 31741 if (respondToWidth > _.breakpoints[breakpoint]) { 31742 targetBreakpoint = _.breakpoints[breakpoint]; 31743 } 31744 } 31745 } 31746 } 31747 31748 if (targetBreakpoint !== null) { 31749 if (_.activeBreakpoint !== null) { 31750 if (targetBreakpoint !== _.activeBreakpoint || forceUpdate) { 31751 _.activeBreakpoint = 31752 targetBreakpoint; 31753 if (_.breakpointSettings[targetBreakpoint] === 'unslick') { 31754 _.unslick(targetBreakpoint); 31755 } else { 31756 _.options = $.extend({}, _.originalSettings, 31757 _.breakpointSettings[ 31758 targetBreakpoint]); 31759 if (initial === true) { 31760 _.currentSlide = _.options.initialSlide; 31761 } 31762 _.refresh(initial); 31763 } 31764 triggerBreakpoint = targetBreakpoint; 31765 } 31766 } else { 31767 _.activeBreakpoint = targetBreakpoint; 31768 if (_.breakpointSettings[targetBreakpoint] === 'unslick') { 31769 _.unslick(targetBreakpoint); 31770 } else { 31771 _.options = $.extend({}, _.originalSettings, 31772 _.breakpointSettings[ 31773 targetBreakpoint]); 31774 if (initial === true) { 31775 _.currentSlide = _.options.initialSlide; 31776 } 31777 _.refresh(initial); 31778 } 31779 triggerBreakpoint = targetBreakpoint; 31780 } 31781 } else { 31782 if (_.activeBreakpoint !== null) { 31783 _.activeBreakpoint = null; 31784 _.options = _.originalSettings; 31785 if (initial === true) { 31786 _.currentSlide = _.options.initialSlide; 31787 } 31788 _.refresh(initial); 31789 triggerBreakpoint = targetBreakpoint; 31790 } 31791 } 31792 31793 // only trigger breakpoints during an actual break. not on initialize. 31794 if( !initial && triggerBreakpoint !== false ) { 31795 _.$slider.trigger('breakpoint', [_, triggerBreakpoint]); 31796 } 31797 } 31798 31799 }; 31800 31801 Slick.prototype.changeSlide = function(event, dontAnimate) { 31802 31803 var _ = this, 31804 $target = $(event.target), 31805 indexOffset, slideOffset, unevenOffset; 31806 31807 // If target is a link, prevent default action. 31808 if($target.is('a')) { 31809 event.preventDefault(); 31810 } 31811 31812 // If target is not the <li> element (ie: a child), find the <li>. 31813 if(!$target.is('li')) { 31814 $target = $target.closest('li'); 31815 } 31816 31817 unevenOffset = (_.slideCount % _.options.slidesToScroll !== 0); 31818 indexOffset = unevenOffset ? 0 : (_.slideCount - _.currentSlide) % _.options.slidesToScroll; 31819 31820 switch (event.data.message) { 31821 31822 case 'previous': 31823 slideOffset = indexOffset === 0 ? _.options.slidesToScroll : _.options.slidesToShow - indexOffset; 31824 if (_.slideCount > _.options.slidesToShow) { 31825 _.slideHandler(_.currentSlide - slideOffset, false, dontAnimate); 31826 } 31827 break; 31828 31829 case 'next': 31830 slideOffset = indexOffset === 0 ? _.options.slidesToScroll : indexOffset; 31831 if (_.slideCount > _.options.slidesToShow) { 31832 _.slideHandler(_.currentSlide + slideOffset, false, dontAnimate); 31833 } 31834 break; 31835 31836 case 'index': 31837 var index = event.data.index === 0 ? 0 : 31838 event.data.index || $target.index() * _.options.slidesToScroll; 31839 31840 _.slideHandler(_.checkNavigable(index), false, dontAnimate); 31841 $target.children().trigger('focus'); 31842 break; 31843 31844 default: 31845 return; 31846 } 31847 31848 }; 31849 31850 Slick.prototype.checkNavigable = function(index) { 31851 31852 var _ = this, 31853 navigables, prevNavigable; 31854 31855 navigables = _.getNavigableIndexes(); 31856 prevNavigable = 0; 31857 if (index > navigables[navigables.length - 1]) { 31858 index = navigables[navigables.length - 1]; 31859 } else { 31860 for (var n in navigables) { 31861 if (index < navigables[n]) { 31862 index = prevNavigable; 31863 break; 31864 } 31865 prevNavigable = navigables[n];
31866 } 31867 } 31868 31869 return index; 31870 }; 31871 31872 Slick.prototype.cleanUpEvents = function() { 31873 31874 var _ = this; 31875 31876 if (_.options.dots && _.$dots !== null) { 31877 31878 $('li', _.$dots).off('click.slick', _.changeSlide); 31879 31880 if (_.options.pauseOnDotsHover === true && _.options.autoplay === true) { 31881 31882 $('li', _.$dots) 31883 .off('mouseenter.slick', $.proxy(_.setPaused, _, true)) 31884 .off('mouseleave.slick', $.proxy(_.setPaused, _, false)); 31885 31886 } 31887 31888 } 31889 31890 if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { 31891 _.$prevArrow && _.$prevArrow.off('click.slick', _.changeSlide); 31892 _.$nextArrow && _.$nextArrow.off('click.slick', _.changeSlide); 31893 } 31894 31895 _.$list.off('touchstart.slick mousedown.slick', _.swipeHandler); 31896 _.$list.off('touchmove.slick mousemove.slick', _.swipeHandler); 31897 _.$list.off('touchend.slick mouseup.slick', _.swipeHandler);
vendor: 5,198 bytes, lines 31898-32098
31898 _.$list.off('touchcancel.slick mouseleave.slick', _.swipeHandler); 31899 31900 _.$list.off('click.slick', _.clickHandler); 31901 31902 $(document).off(_.visibilityChange, _.visibility); 31903 31904 _.$list.off('mouseenter.slick', $.proxy(_.setPaused, _, true)); 31905 _.$list.off('mouseleave.slick', $.proxy(_.setPaused, _, false)); 31906 31907 if (_.options.accessibility === true) { 31908 _.$list.off('keydown.slick', _.keyHandler); 31909 } 31910 31911 if (_.options.focusOnSelect === true) { 31912 $(_.$slideTrack).children().off('click.slick', _.selectHandler); 31913 } 31914 31915 $(window).off('orientationchange.slick.slick-' + _.instanceUid, _.orientationChange); 31916 31917 $(window).off('resize.slick.slick-' + _.instanceUid, _.resize); 31918 31919 $('[draggable!=true]', _.$slideTrack).off('dragstart', _.preventDefault); 31920 31921 $(window).off('load.slick.slick-' + _.instanceUid, _.setPosition); 31922 $(document).off('ready.slick.slick-' + _.instanceUid, _.setPosition); 31923 }; 31924 31925 Slick.prototype.cleanUpRows = function() { 31926 31927 var _ = this, originalSlides; 31928 31929 if(_.options.rows > 1) { 31930 originalSlides = _.$slides.children().children(); 31931 originalSlides.removeAttr('style'); 31932 _.$slider.html(originalSlides); 31933 } 31934 31935 }; 31936 31937 Slick.prototype.clickHandler = function(event) { 31938 31939 var _ = this; 31940 31941 if (_.shouldClick === false) { 31942 event.stopImmediatePropagation(); 31943 event.stopPropagation(); 31944 event.preventDefault(); 31945 } 31946 31947 }; 31948 31949 Slick.prototype.destroy = function(refresh) { 31950 31951 var _ = this; 31952 31953 _.autoPlayClear(); 31954 31955 _.touchObject = {}; 31956 31957 _.cleanUpEvents(); 31958 31959 $('.slick-cloned', _.$slider).detach(); 31960 31961 if (_.$dots) { 31962 _.$dots.remove(); 31963 } 31964 31965 31966 if ( _.$prevArrow && _.$prevArrow.length ) { 31967 31968 _.$prevArrow 31969 .removeClass('slick-disabled slick-arrow slick-hidden') 31970 .removeAttr('aria-hidden aria-disabled tabindex') 31971 .css("display",""); 31972 31973 if ( _.htmlExpr.test( _.options.prevArrow )) { 31974 _.$prevArrow.remove(); 31975 } 31976 } 31977 31978 if ( _.$nextArrow && _.$nextArrow.length ) { 31979 31980 _.$nextArrow 31981 .removeClass('slick-disabled slick-arrow slick-hidden') 31982 .removeAttr('aria-hidden aria-disabled tabindex') 31983 .css("display",""); 31984 31985 if ( _.htmlExpr.test( _.options.nextArrow )) { 31986 _.$nextArrow.remove(); 31987 } 31988 31989 } 31990 31991 31992 if (_.$slides) { 31993 31994 _.$slides 31995 .removeClass('slick-slide slick-active slick-center slick-visible slick-current') 31996 .removeAttr('aria-hidden') 31997 .removeAttr('data-slick-index') 31998 .each(function(){ 31999 $(this).attr('style', $(this).data('originalStyling')); 32000 }); 32001 32002 _.$slideTrack.children(this.options.slide).detach(); 32003 32004 _.$slideTrack.detach(); 32005 32006 _.$list.detach(); 32007 32008 _.$slider.append(_.$slides); 32009 } 32010 32011 _.cleanUpRows(); 32012 32013 _.$slider.removeClass('slick-slider'); 32014 _.$slider.removeClass('slick-initialized'); 32015 32016 _.unslicked = true; 32017 32018 if(!refresh) { 32019 _.$slider.trigger('destroy', [_]); 32020 } 32021 32022 }; 32023 32024 Slick.prototype.disableTransition = function(slide) { 32025 32026 var _ = this, 32027 transition = {}; 32028 32029 transition[_.transitionType] = ''; 32030 32031 if (_.options.fade === false) { 32032 _.$slideTrack.css(transition); 32033 } else { 32034 _.$slides.eq(slide).css(transition); 32035 } 32036 32037 }; 32038 32039 Slick.prototype.fadeSlide = function(slideIndex, callback) { 32040 32041 var _ = this; 32042 32043 if (_.cssTransitions === false) { 32044 32045 _.$slides.eq(slideIndex).css({ 32046 zIndex: _.options.zIndex 32047 }); 32048 32049 _.$slides.eq(slideIndex).animate({ 32050 opacity: 1 32051 }, _.options.speed, _.options.easing, callback); 32052 32053 } else { 32054 32055 _.applyTransition(slideIndex); 32056 32057 _.$slides.eq(slideIndex).css({ 32058 opacity: 1, 32059 zIndex: _.options.zIndex 32060 }); 32061 32062 if (callback) { 32063 setTimeout(function() { 32064 32065 _.disableTransition(slideIndex); 32066 32067 callback.call(); 32068 }, _.options.speed); 32069 } 32070 32071 } 32072 32073 }; 32074 32075 Slick.prototype.fadeSlideOut = function(slideIndex) { 32076 32077 var _ = this; 32078 32079 if (_.cssTransitions === false) { 32080 32081 _.$slides.eq(slideIndex).animate({ 32082 opacity: 0, 32083 zIndex: _.options.zIndex - 2 32084 }, _.options.speed, _.options.easing); 32085 32086 } else { 32087 32088 _.applyTransition(slideIndex); 32089 32090 _.$slides.eq(slideIndex).css({ 32091 opacity: 0, 32092 zIndex: _.options.zIndex - 2 32093 }); 32094 32095 } 32096 32097 }; 32098
32099 Slick.prototype.filterSlides = Slick.prototype.slickFilter = function(filter) { 32100 32101 var _ = this; 32102 32103 if (filter !== null) { 32104 32105 _.unload(); 32106 32107 _.$slideTrack.children(this.options.slide).detach(); 32108 32109 _.$slidesCache.filter(filter).appendTo(_.$slideTrack); 32110 32111 _.reinit(); 32112 32113 } 32114 32115 }; 32116 32117 Slick.prototype.getCurrent = Slick.prototype.slickCurrentSlide = function() { 32118 32119 var _ = this; 32120 return _.currentSlide; 32121 32122 }; 32123 32124 Slick.prototype.getDotCount = function() { 32125 32126 var _ = this; 32127 32128 var breakPoint = 0; 32129 var counter = 0; 32130 var pagerQty = 0; 32131 32132 if (_.options.infinite === true) { 32133 while (breakPoint < _.slideCount) { 32134 ++pagerQty; 32135 breakPoint = counter + _.options.slidesToShow; 32136 counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; 32137 } 32138 } else if (_.options.centerMode === true) { 32139 pagerQty = _.slideCount; 32140 } else { 32141 while (breakPoint < _.slideCount) { 32142 ++pagerQty; 32143 breakPoint = counter + _.options.slidesToShow; 32144 counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; 32145 } 32146 } 32147 32148 return pagerQty - 1; 32149 32150 }; 32151 32152 Slick.prototype.getLeft = function(slideIndex) { 32153 32154 var _ = this, 32155 targetLeft, 32156 verticalHeight, 32157 verticalOffset = 0, 32158 targetSlide; 32159 32160 _.slideOffset = 0; 32161 verticalHeight = _.$slides.first().outerHeight(true); 32162 32163 if (_.options.infinite === true) { 32164 if (_.slideCount > _.options.slidesToShow) { 32165 _.slideOffset = (_.slideWidth * _.options.slidesToShow) * -1; 32166 verticalOffset = (verticalHeight * _.options.slidesToShow) * -1; 32167 } 32168 if (_.slideCount % _.options.slidesToScroll !== 0) { 32169 if (slideIndex + _.options.slidesToScroll > _.slideCount && _.slideCount > _.options.slidesToShow) { 32170 if (slideIndex > _.slideCount) { 32171 _.slideOffset = ((_.options.slidesToShow - (sl
vendor: 10,685 bytes, lines 32171-32501
32171ideIndex - _.slideCount)) * _.slideWidth) * -1; 32172 verticalOffset = ((_.options.slidesToShow - (slideIndex - _.slideCount)) * verticalHeight) * -1; 32173 } else { 32174 _.slideOffset = ((_.slideCount % _.options.slidesToScroll) * _.slideWidth) * -1; 32175 verticalOffset = ((_.slideCount % _.options.slidesToScroll) * verticalHeight) * -1; 32176 } 32177 } 32178 } 32179 } else { 32180 if (slideIndex + _.options.slidesToShow > _.slideCount) { 32181 _.slideOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * _.slideWidth; 32182 verticalOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * verticalHeight; 32183 } 32184 } 32185 32186 if (_.slideCount <= _.options.slidesToShow) { 32187 _.slideOffset = 0; 32188 verticalOffset = 0; 32189 } 32190 32191 if (_.options.centerMode === true && _.options.infinite === true) { 32192 _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2) - _.slideWidth; 32193 } else if (_.options.centerMode === true) { 32194 _.slideOffset = 0; 32195 _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2); 32196 } 32197 32198 if (_.options.vertical === false) { 32199 targetLeft = ((slideIndex * _.slideWidth) * -1) + _.slideOffset; 32200 } else { 32201 targetLeft = ((slideIndex * verticalHeight) * -1) + verticalOffset; 32202 } 32203 32204 if (_.options.variableWidth === true) { 32205 32206 if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) { 32207 targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex); 32208 } else { 32209 targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow); 32210 } 32211 32212 targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0; 32213 32214 if (_.options.centerMode === true) { 32215 if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) { 32216 targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex); 32217 } else { 32218 targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow + 1); 32219 } 32220 targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0; 32221 targetLeft += (_.$list.width() - targetSlide.outerWidth()) / 2; 32222 } 32223 } 32224 32225 return targetLeft; 32226 32227 }; 32228 32229 Slick.prototype.getOption = Slick.prototype.slickGetOption = function(option) { 32230 32231 var _ = this; 32232 32233 return _.options[option]; 32234 32235 }; 32236 32237 Slick.prototype.getNavigableIndexes = function() { 32238 32239 var _ = this, 32240 breakPoint = 0, 32241 counter = 0, 32242 indexes = [], 32243 max; 32244 32245 if (_.options.infinite === false) { 32246 max = _.slideCount; 32247 } else { 32248 breakPoint = _.options.slidesToScroll * -1; 32249 counter = _.options.slidesToScroll * -1; 32250 max = _.slideCount * 2; 32251 } 32252 32253 while (breakPoint < max) { 32254 indexes.push(breakPoint); 32255 breakPoint = counter + _.options.slidesToScroll; 32256 counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow; 32257 } 32258 32259 return indexes; 32260 32261 }; 32262 32263 Slick.prototype.getSlick = function() { 32264 32265 return this; 32266 32267 }; 32268 32269 Slick.prototype.getSlideCount = function() { 32270 32271 var _ = this, 32272 slidesTraversed, swipedSlide, centerOffset; 32273 32274 centerOffset = _.options.centerMode === true ? _.slideWidth * Math.floor(_.options.slidesToShow / 2) : 0; 32275 32276 if (_.options.swipeToSlide === true) { 32277 _.$slideTrack.find('.slick-slide').each(function(index, slide) { 32278 if (slide.offsetLeft - centerOffset + ($(slide).outerWidth() / 2) > (_.swipeLeft * -1)) { 32279 swipedSlide = slide; 32280 return false; 32281 } 32282 }); 32283 32284 slidesTraversed = Math.abs($(swipedSlide).attr('data-slick-index') - _.currentSlide) || 1; 32285 32286 return slidesTraversed; 32287 32288 } else { 32289 return _.options.slidesToScroll; 32290 } 32291 32292 }; 32293 32294 Slick.prototype.goTo = Slick.prototype.slickGoTo = function(slide, dontAnimate) { 32295 32296 var _ = this; 32297 32298 _.changeSlide({ 32299 data: { 32300 message: 'index', 32301 index: parseInt(slide) 32302 } 32303 }, dontAnimate); 32304 32305 }; 32306 32307 Slick.prototype.init = function(creation) { 32308 32309 var _ = this; 32310 32311 if (!$(_.$slider).hasClass('slick-initialized')) { 32312 32313 $(_.$slider).addClass('slick-initialized'); 32314 32315 _.buildRows(); 32316 _.buildOut(); 32317 _.setProps(); 32318 _.startLoad(); 32319 _.loadSlider(); 32320 _.initializeEvents(); 32321 _.updateArrows(); 32322 _.updateDots(); 32323 32324 } 32325 32326 if (creation) { 32327 _.$slider.trigger('init', [_]); 32328 } 32329 32330 if (_.options.accessibility === true) { 32331 _.initADA(); 32332 } 32333 32334 }; 32335 32336 Slick.prototype.initArrowEvents = function() { 32337 32338 var _ = this; 32339 32340 if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { 32341 _.$prevArrow.on('click.slick', { 32342 message: 'previous' 32343 }, _.changeSlide); 32344 _.$nextArrow.on('click.slick', { 32345 message: 'next' 32346 }, _.changeSlide); 32347 } 32348 32349 }; 32350 32351 Slick.prototype.initDotEvents = function() { 32352 32353 var _ = this; 32354 32355 if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { 32356 $('li', _.$dots).on('click.slick', { 32357 message: 'index' 32358 }, _.changeSlide); 32359 } 32360 32361 if (_.options.dots === true && _.options.pauseOnDotsHover === true && _.options.autoplay === true) { 32362 $('li', _.$dots) 32363 .on('mouseenter.slick', $.proxy(_.setPaused, _, true)) 32364 .on('mouseleave.slick', $.proxy(_.setPaused, _, false)); 32365 } 32366 32367 }; 32368 32369 Slick.prototype.initializeEvents = function() { 32370 32371 var _ = this; 32372 32373 _.initArrowEvents(); 32374 32375 _.initDotEvents(); 32376 32377 _.$list.on('touchstart.slick mousedown.slick', { 32378 action: 'start' 32379 }, _.swipeHandler); 32380 _.$list.on('touchmove.slick mousemove.slick', { 32381 action: 'move' 32382 }, _.swipeHandler); 32383 _.$list.on('touchend.slick mouseup.slick', { 32384 action: 'end' 32385 }, _.swipeHandler); 32386 _.$list.on('touchcancel.slick mouseleave.slick', { 32387 action: 'end' 32388 }, _.swipeHandler); 32389 32390 _.$list.on('click.slick', _.clickHandler); 32391 32392 $(document).on(_.visibilityChange, $.proxy(_.visibility, _)); 32393 32394 _.$list.on('mouseenter.slick', $.proxy(_.setPaused, _, true)); 32395 _.$list.on('mouseleave.slick', $.proxy(_.setPaused, _, false)); 32396 32397 if (_.options.accessibility === true) { 32398 _.$list.on('keydown.slick', _.keyHandler); 32399 } 32400 32401 if (_.options.focusOnSelect === true) { 32402 $(_.$slideTrack).children().on('click.slick', _.selectHandler); 32403 } 32404 32405 $(window).on('orientationchange.slick.slick-' + _.instanceUid, $.proxy(_.orientationChange, _)); 32406 32407 $(window).on('resize.slick.slick-' + _.instanceUid, $.proxy(_.resize, _)); 32408 32409 $('[draggable!=true]', _.$slideTrack).on('dragstart', _.preventDefault); 32410 32411 $(window).on('load.slick.slick-' + _.instanceUid, _.setPosition); 32412 $(document).on('ready.slick.slick-' + _.instanceUid, _.setPosition); 32413 32414 }; 32415 32416 Slick.prototype.initUI = function() { 32417 32418 var _ = this; 32419 32420 if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { 32421 32422 _.$prevArrow.show(); 32423 _.$nextArrow.show(); 32424 32425 } 32426 32427 if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { 32428 32429 _.$dots.show(); 32430 32431 } 32432 32433 if (_.options.autoplay === true) { 32434 32435 _.autoPlay(); 32436 32437 } 32438 32439 }; 32440 32441 Slick.prototype.keyHandler = function(event) { 32442 32443 var _ = this; 32444 //Dont slide if the cursor is inside the form fields and arrow keys are pressed 32445 if(!event.target.tagName.match('TEXTAREA|INPUT|SELECT')) { 32446 if (event.keyCode === 37 && _.options.accessibility === true) { 32447 _.changeSlide({ 32448 data: { 32449 message: 'previous' 32450 } 32451 }); 32452 } else if (event.keyCode === 39 && _.options.accessibility === true) { 32453 _.changeSlide({ 32454 data: { 32455 message: 'next' 32456 } 32457 }); 32458 } 32459 } 32460 32461 }; 32462 32463 Slick.prototype.lazyLoad = function() { 32464 32465 var _ = this, 32466 loadRange, cloneRange, rangeStart, rangeEnd; 32467 32468 function loadImages(imagesScope) { 32469 $('img[data-lazy]', imagesScope).each(function() { 32470 32471 var image = $(this), 32472 imageSource = $(this).attr('data-lazy'), 32473 imageToLoad = document.createElement('img'); 32474 32475 imageToLoad.onload = function() { 32476 image 32477 .animate({ opacity: 0 }, 100, function() { 32478 image 32479 .attr('src', imageSource) 32480 .animate({ opacity: 1 }, 200, function() { 32481 image 32482 .removeAttr('data-lazy') 32483 .removeClass('slick-loading'); 32484 }); 32485 }); 32486 }; 32487 32488 imageToLoad.src = imageSource; 32489 32490 }); 32491 } 32492 32493 if (_.options.centerMode === true) { 32494 if (_.options.infinite === true) { 32495 rangeStart = _.currentSlide + (_.options.slidesToShow / 2 + 1); 32496 rangeEnd = rangeStart + _.options.slidesToShow + 2; 32497 } else { 32498 rangeStart = Math.max(0, _.currentSlide - (_.options.slidesToShow / 2 + 1)); 32499 rangeEnd = 2 + (_.options.slidesToShow / 2 + 1) + _.currentSlide; 32500 } 32501 } else {
32502 rangeStart = _.options.infinite ? _.options.slidesToShow + _.currentSlide : _.currentSlide; 32503 rangeEnd = rangeStart + _.options.slidesToShow; 32504 if (_.options.fade === true) { 32505 if (rangeStart > 0) rangeStart--; 32506 if (rangeEnd <= _.slideCount) rangeEnd++; 32507 } 32508 } 32509 32510 loadRange = _.$slider.find('.slick-slide').slice(rangeStart, rangeEnd); 32511 loadImages(loadRange); 32512 32513 if (_.slideCount <= _.options.slidesToShow) { 32514 cloneRange = _.$slider.find('.slick-slide'); 32515 loadImages(cloneRange); 32516 } else 32517 if (_.currentSlide >= _.slideCount - _.options.slidesToShow) { 32518 cloneRange = _.$slider.find('.slick-cloned').slice(0, _.options.slidesToShow); 32519 loadImages(cloneRange); 32520 } else if (_.currentSlide === 0) { 32521 cloneRange = _.$slider.find('.slick-cloned').slice(_.options.slidesToShow * -1); 32522 loadImages(cloneRange); 32523 } 32524 32525 }; 32526 32527 Slick.prototype.loadSlider = function() { 32528 32529 var _ = this; 32530 32531 _.setPosition(); 32532 32533 _.$slideTrack.css({ 32534 opacity: 1 32535 }); 32536 32537 _.$slider.removeClass('slick-loading'); 32538 32539 _.initUI(); 32540 32541 if (_.options.lazyLoad === 'progressive') { 32542 _.progressiveLazyLoad(); 32543 } 32544 32545 }; 32546 32547 Slick.prototype.next = Slick.prototype.slickNext = function() { 32548 32549 var _ = this; 32550 32551 _.changeSlide({ 32552 data: { 32553 message: 'next' 32554 } 32555 }); 32556 32557 }; 32558 32559 Slick.prototype.orientationChange = function() { 32560 32561 var _ = this; 32562 32563 _.checkResponsive(); 32564 _.setPosition(); 32565 32566 }; 32567 32568 Slick.prototype.pause = Slick.prototype.slickPause = function() { 32569 32570 var _ = this; 32571 32572 _.autoPlayClear(); 32573 _.paused = true; 32574 32575 }; 32576 32577 Slick.prototype.play = Slick.prototype.slickPlay = function() { 32578 32579 var _ = this; 32580 32581 _.paused = false; 32582 _.autoPlay(); 32583 32584 }; 32585 32586 Slick.prototype.postSlide = function(index) { 32587 32588 var _ = this; 32589 32590 _.$slider.trigger('afterChange', [_, index]); 32591 32592 _.animating = false; 32593 32594 _.setPosition(); 32595 32596 _.swipeLeft = null; 32597 32598 if (_.options.autoplay === true && _.paused === false) { 32599 _.autoPlay(); 32600 } 32601 if (_.options.accessibility === true) { 32602 _.initADA(); 32603 } 32604 32605 }; 32606 32607 Slick.prototype.prev = Slick.prototype.slickPrev = function() { 32608 32609 var _ = this; 32610 32611 _.changeSlide({ 32612 data: { 32613 message: 'previous' 32614 } 32615 }); 32616 32617 }; 32618 32619 Slick.prototype.preventDefault = function(event) { 32620 event.preventDefault(); 32621 }; 32622 32623 Slick.prototype.progressiveLazyLoad = function() { 32624 32625 var _ = this, 32626 imgCount, targetImage; 32627 32628 imgCount = $('img[data-lazy]', _.$slider).length; 32629 32630 if (imgCount > 0) { 32631 targetImage = $('img[data-lazy]', _.$slider).first(); 32632 targetImage.attr('src', null); 32633 targetImage.attr('src', targetImage.attr('data-lazy')).removeClass('slick-loading').load(function() { 32634 targetImage.removeAttr('data-lazy'); 32635 _.progressiveLazyLoad(); 32636 32637 if (_.options.adaptiveHeight === true) { 32638 _.setPosition(); 32639 } 32640 }) 32641 .error(function() { 32642 targetImage.removeAttr('data-lazy'); 32643 _.progressiveLazyLoad(); 32644 }); 32645 } 32646 32647 }; 32648 32649 Slick.prototype.refresh = function( initializing ) { 32650 32651 var _ = this, currentSlide, firstVisible; 32652 32653 firstVisible = _.slideCount - _.options.slidesToShow; 32654 32655 // check that the new breakpoint can actually accept the 32656 // "current slide" as the current slide, otherwise we need 32657 // to set it to the closest possible value. 32658 if ( !_.options.infinite ) { 32659 if ( _.slideCount <= _.options.slidesToShow ) { 32660 _.currentSlide = 0; 32661 } else if ( _.currentSlide > firstVisible ) { 32662 _.currentSlide = firstVisible; 32663 } 32664 } 32665 32666 currentSlide = _.currentSlide; 32667 32668 _.destroy(true); 32669 32670 $.extend(_, _.initials, { currentSlide: currentSlide }); 32671
vendor: 29,404 bytes, lines 32672-33624
32672 _.init(); 32673 32674 if( !initializing ) { 32675 32676 _.changeSlide({ 32677 data: { 32678 message: 'index', 32679 index: currentSlide 32680 } 32681 }, false); 32682 32683 } 32684 32685 }; 32686 32687 Slick.prototype.registerBreakpoints = function() { 32688 32689 var _ = this, breakpoint, currentBreakpoint, l, 32690 responsiveSettings = _.options.responsive || null; 32691 32692 if ( $.type(responsiveSettings) === "array" && responsiveSettings.length ) { 32693 32694 _.respondTo = _.options.respondTo || 'window'; 32695 32696 for ( breakpoint in responsiveSettings ) { 32697 32698 l = _.breakpoints.length-1; 32699 currentBreakpoint = responsiveSettings[breakpoint].breakpoint; 32700 32701 if (responsiveSettings.hasOwnProperty(breakpoint)) { 32702 32703 // loop through the breakpoints and cut out any existing 32704 // ones with the same breakpoint number, we don't want dupes. 32705 while( l >= 0 ) { 32706 if( _.breakpoints[l] && _.breakpoints[l] === currentBreakpoint ) { 32707 _.breakpoints.splice(l,1); 32708 } 32709 l--; 32710 } 32711 32712 _.breakpoints.push(currentBreakpoint); 32713 _.breakpointSettings[currentBreakpoint] = responsiveSettings[breakpoint].settings; 32714 32715 } 32716 32717 } 32718 32719 _.breakpoints.sort(function(a, b) { 32720 return ( _.options.mobileFirst ) ? a-b : b-a; 32721 }); 32722 32723 } 32724 32725 }; 32726 32727 Slick.prototype.reinit = function() { 32728 32729 var _ = this; 32730 32731 _.$slides = 32732 _.$slideTrack 32733 .children(_.options.slide) 32734 .addClass('slick-slide'); 32735 32736 _.slideCount = _.$slides.length; 32737 32738 if (_.currentSlide >= _.slideCount && _.currentSlide !== 0) { 32739 _.currentSlide = _.currentSlide - _.options.slidesToScroll; 32740 } 32741 32742 if (_.slideCount <= _.options.slidesToShow) { 32743 _.currentSlide = 0; 32744 } 32745 32746 _.registerBreakpoints(); 32747 32748 _.setProps(); 32749 _.setupInfinite(); 32750 _.buildArrows(); 32751 _.updateArrows(); 32752 _.initArrowEvents(); 32753 _.buildDots(); 32754 _.updateDots(); 32755 _.initDotEvents(); 32756 32757 _.checkResponsive(false, true); 32758 32759 if (_.options.focusOnSelect === true) { 32760 $(_.$slideTrack).children().on('click.slick', _.selectHandler); 32761 } 32762 32763 _.setSlideClasses(0); 32764 32765 _.setPosition(); 32766 32767 _.$slider.trigger('reInit', [_]); 32768 32769 if (_.options.autoplay === true) { 32770 _.focusHandler(); 32771 } 32772 32773 }; 32774 32775 Slick.prototype.resize = function() { 32776 32777 var _ = this; 32778 32779 if ($(window).width() !== _.windowWidth) { 32780 clearTimeout(_.windowDelay); 32781 _.windowDelay = window.setTimeout(function() { 32782 _.windowWidth = $(window).width(); 32783 _.checkResponsive(); 32784 if( !_.unslicked ) { _.setPosition(); } 32785 }, 50); 32786 } 32787 }; 32788 32789 Slick.prototype.removeSlide = Slick.prototype.slickRemove = function(index, removeBefore, removeAll) { 32790 32791 var _ = this; 32792 32793 if (typeof(index) === 'boolean') { 32794 removeBefore = index; 32795 index = removeBefore === true ? 0 : _.slideCount - 1; 32796 } else { 32797 index = removeBefore === true ? --index : index; 32798 } 32799 32800 if (_.slideCount < 1 || index < 0 || index > _.slideCount - 1) { 32801 return false; 32802 } 32803 32804 _.unload(); 32805 32806 if (removeAll === true) { 32807 _.$slideTrack.children().remove(); 32808 } else { 32809 _.$slideTrack.children(this.options.slide).eq(index).remove(); 32810 } 32811 32812 _.$slides = _.$slideTrack.children(this.options.slide); 32813 32814 _.$slideTrack.children(this.options.slide).detach(); 32815 32816 _.$slideTrack.append(_.$slides); 32817 32818 _.$slidesCache = _.$slides; 32819 32820 _.reinit(); 32821 32822 }; 32823 32824 Slick.prototype.setCSS = function(position) { 32825 32826 var _ = this, 32827 positionProps = {}, 32828 x, y; 32829 32830 if (_.options.rtl === true) { 32831 position = -position; 32832 } 32833 x = _.positionProp == 'left' ? Math.ceil(position) + 'px' : '0px'; 32834 y = _.positionProp == 'top' ? Math.ceil(position) + 'px' : '0px'; 32835 32836 positionProps[_.positionProp] = position; 32837 32838 if (_.transformsEnabled === false) { 32839 _.$slideTrack.css(positionProps); 32840 } else { 32841 positionProps = {}; 32842 if (_.cssTransitions === false) { 32843 positionProps[_.animType] = 'translate(' + x + ', ' + y + ')'; 32844 _.$slideTrack.css(positionProps); 32845 } else { 32846 positionProps[_.animType] = 'translate3d(' + x + ', ' + y + ', 0px)'; 32847 _.$slideTrack.css(positionProps); 32848 } 32849 } 32850 32851 }; 32852 32853 Slick.prototype.setDimensions = function() { 32854 32855 var _ = this; 32856 32857 if (_.options.vertical === false) { 32858 if (_.options.centerMode === true) { 32859 _.$list.css({ 32860 padding: ('0px ' + _.options.centerPadding) 32861 }); 32862 } 32863 } else { 32864 _.$list.height(_.$slides.first().outerHeight(true) * _.options.slidesToShow); 32865 if (_.options.centerMode === true) { 32866 _.$list.css({ 32867 padding: (_.options.centerPadding + ' 0px') 32868 }); 32869 } 32870 } 32871 32872 _.listWidth = _.$list.width(); 32873 _.listHeight = _.$list.height(); 32874 32875 32876 if (_.options.vertical === false && _.options.variableWidth === false) { 32877 _.slideWidth = Math.ceil(_.listWidth / _.options.slidesToShow); 32878 _.$slideTrack.width(Math.ceil((_.slideWidth * _.$slideTrack.children('.slick-slide').length))); 32879 32880 } else if (_.options.variableWidth === true) { 32881 _.$slideTrack.width(5000 * _.slideCount); 32882 } else { 32883 _.slideWidth = Math.ceil(_.listWidth); 32884 _.$slideTrack.height(Math.ceil((_.$slides.first().outerHeight(true) * _.$slideTrack.children('.slick-slide').length))); 32885 } 32886 32887 var offset = _.$slides.first().outerWidth(true) - _.$slides.first().width(); 32888 if (_.options.variableWidth === false) _.$slideTrack.children('.slick-slide').width(_.slideWidth - offset); 32889 32890 }; 32891 32892 Slick.prototype.setFade = function() { 32893 32894 var _ = this, 32895 targetLeft; 32896 32897 _.$slides.each(function(index, element) { 32898 targetLeft = (_.slideWidth * index) * -1; 32899 if (_.options.rtl === true) { 32900 $(element).css({ 32901 position: 'relative', 32902 right: targetLeft, 32903 top: 0, 32904 zIndex: _.options.zIndex - 2, 32905 opacity: 0 32906 }); 32907 } else { 32908 $(element).css({ 32909 position: 'relative', 32910 left: targetLeft, 32911 top: 0, 32912 zIndex: _.options.zIndex - 2, 32913 opacity: 0 32914 }); 32915 } 32916 }); 32917 32918 _.$slides.eq(_.currentSlide).css({ 32919 zIndex: _.options.zIndex - 1, 32920 opacity: 1 32921 }); 32922 32923 }; 32924 32925 Slick.prototype.setHeight = function() { 32926 32927 var _ = this; 32928 32929 if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) { 32930 var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true); 32931 _.$list.css('height', targetHeight); 32932 } 32933 32934 }; 32935 32936 Slick.prototype.setOption = Slick.prototype.slickSetOption = function(option, value, refresh) { 32937 32938 var _ = this, l, item; 32939 32940 if( option === "responsive" && $.type(value) === "array" ) { 32941 for ( item in value ) { 32942 if( $.type( _.options.responsive ) !== "array" ) { 32943 _.options.responsive = [ value[item] ]; 32944 } else { 32945 l = _.options.responsive.length-1; 32946 // loop through the responsive object and splice out duplicates. 32947 while( l >= 0 ) { 32948 if( _.options.responsive[l].breakpoint === value[item].breakpoint ) { 32949 _.options.responsive.splice(l,1); 32950 } 32951 l--; 32952 } 32953 _.options.responsive.push( value[item] ); 32954 } 32955 } 32956 } else { 32957 _.options[option] = value; 32958 } 32959 32960 if (refresh === true) { 32961 _.unload(); 32962 _.reinit(); 32963 } 32964 32965 }; 32966 32967 Slick.prototype.setPosition = function() { 32968 32969 var _ = this; 32970 32971 _.setDimensions(); 32972 32973 _.setHeight(); 32974 32975 if (_.options.fade === false) { 32976 _.setCSS(_.getLeft(_.currentSlide)); 32977 } else { 32978 _.setFade(); 32979 } 32980 32981 _.$slider.trigger('setPosition', [_]); 32982 32983 }; 32984 32985 Slick.prototype.setProps = function() { 32986 32987 var _ = this, 32988 bodyStyle = document.body.style; 32989 32990 _.positionProp = _.options.vertical === true ? 'top' : 'left'; 32991 32992 if (_.positionProp === 'top') { 32993 _.$slider.addClass('slick-vertical'); 32994 } else { 32995 _.$slider.removeClass('slick-vertical'); 32996 } 32997 32998 if (bodyStyle.WebkitTransition !== undefined || 32999 bodyStyle.MozTransition !== undefined || 33000 bodyStyle.msTransition !== undefined) { 33001 if (_.options.useCSS === true) { 33002 _.cssTransitions = true; 33003 } 33004 } 33005 33006 if ( _.options.fade ) { 33007 if ( typeof _.options.zIndex === 'number' ) { 33008 if( _.options.zIndex < 3 ) { 33009 _.options.zIndex = 3; 33010 } 33011 } else { 33012 _.options.zIndex = _.defaults.zIndex; 33013 } 33014 } 33015 33016 if (bodyStyle.OTransform !== undefined) { 33017 _.animType = 'OTransform'; 33018 _.transformType = '-o-transform'; 33019 _.transitionType = 'OTransition'; 33020 if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; 33021 } 33022 if (bodyStyle.MozTransform !== undefined) { 33023 _.animType = 'MozTransform'; 33024 _.transformType = '-moz-transform'; 33025 _.transitionType = 'MozTransition'; 33026 if (bodyStyle.perspectiveProperty === undefined && bodyStyle.MozPerspective === undefined) _.animType = false; 33027 } 33028 if (bodyStyle.webkitTransform !== undefined) { 33029 _.animType = 'webkitTransform'; 33030 _.transformType = '-webkit-transform'; 33031 _.transitionType = 'webkitTransition'; 33032 if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false; 33033 } 33034 if (bodyStyle.msTransform !== undefined) { 33035 _.animType = 'msTransform'; 33036 _.transformType = '-ms-transform'; 33037 _.transitionType = 'msTransition'; 33038 if (bodyStyle.msTransform === undefined) _.animType = false; 33039 } 33040 if (bodyStyle.transform !== undefined && _.animType !== false) { 33041 _.animType = 'transform'; 33042 _.transformType = 'transform'; 33043 _.transitionType = 'transition'; 33044 } 33045 _.transformsEnabled = _.options.useTransform && (_.animType !== null && _.animType !== false); 33046 }; 33047 33048 33049 Slick.prototype.setSlideClasses = function(index) { 33050 33051 var _ = this, 33052 centerOffset, allSlides, indexOffset, remainder; 33053 33054 allSlides = _.$slider 33055 .find('.slick-slide') 33056 .removeClass('slick-active slick-center slick-current') 33057 .attr('aria-hidden', 'true'); 33058 33059 _.$slides 33060 .eq(index) 33061 .addClass('slick-current'); 33062 33063 if (_.options.centerMode === true) { 33064 33065 centerOffset = Math.floor(_.options.slidesToShow / 2); 33066 33067 if (_.options.infinite === true) { 33068 33069 if (index >= centerOffset && index <= (_.slideCount - 1) - centerOffset) { 33070 33071 _.$slides 33072 .slice(index - centerOffset, index + centerOffset + 1) 33073 .addClass('slick-active') 33074 .attr('aria-hidden', 'false'); 33075 33076 } else { 33077 33078 indexOffset = _.options.slidesToShow + index; 33079 allSlides 33080 .slice(indexOffset - centerOffset + 1, indexOffset + centerOffset + 2) 33081 .addClass('slick-active') 33082 .attr('aria-hidden', 'false'); 33083 33084 } 33085 33086 if (index === 0) { 33087 33088 allSlides 33089 .eq(allSlides.length - 1 - _.options.slidesToShow) 33090 .addClass('slick-center'); 33091 33092 } else if (index === _.slideCount - 1) { 33093 33094 allSlides 33095 .eq(_.options.slidesToShow) 33096 .addClass('slick-center'); 33097 33098 } 33099 33100 } 33101 33102 _.$slides 33103 .eq(index) 33104 .addClass('slick-center'); 33105 33106 } else { 33107 33108 if (index >= 0 && index <= (_.slideCount - _.options.slidesToShow)) { 33109 33110 _.$slides 33111 .slice(index, index + _.options.slidesToShow) 33112 .addClass('slick-active') 33113 .attr('aria-hidden', 'false'); 33114 33115 } else if (allSlides.length <= _.options.slidesToShow) { 33116 33117 allSlides 33118 .addClass('slick-active') 33119 .attr('aria-hidden', 'false'); 33120 33121 } else { 33122 33123 remainder = _.slideCount % _.options.slidesToShow; 33124 indexOffset = _.options.infinite === true ? _.options.slidesToShow + index : index; 33125 33126 if (_.options.slidesToShow == _.options.slidesToScroll && (_.slideCount - index) < _.options.slidesToShow) { 33127 33128 allSlides 33129 .slice(indexOffset - (_.options.slidesToShow - remainder), indexOffset + remainder) 33130 .addClass('slick-active') 33131 .attr('aria-hidden', 'false'); 33132 33133 } else { 33134 33135 allSlides 33136 .slice(indexOffset, indexOffset + _.options.slidesToShow) 33137 .addClass('slick-active') 33138 .attr('aria-hidden', 'false'); 33139 33140 } 33141 33142 } 33143 33144 } 33145 33146 if (_.options.lazyLoad === 'ondemand') { 33147 _.lazyLoad(); 33148 } 33149 33150 }; 33151 33152 Slick.prototype.setupInfinite = function() { 33153 33154 var _ = this, 33155 i, slideIndex, infiniteCount; 33156 33157 if (_.options.fade === true) { 33158 _.options.centerMode = false; 33159 } 33160 33161 if (_.options.infinite === true && _.options.fade === false) { 33162 33163 slideIndex = null; 33164 33165 if (_.slideCount > _.options.slidesToShow) { 33166 33167 if (_.options.centerMode === true) { 33168 infiniteCount = _.options.slidesToShow + 1; 33169 } else { 33170 infiniteCount = _.options.slidesToShow; 33171 } 33172 33173 for (i = _.slideCount; i > (_.slideCount - 33174 infiniteCount); i -= 1) { 33175 slideIndex = i - 1; 33176 $(_.$slides[slideIndex]).clone(true).attr('id', '') 33177 .attr('data-slick-index', slideIndex - _.slideCount) 33178 .prependTo(_.$slideTrack).addClass('slick-cloned'); 33179 } 33180 for (i = 0; i < infiniteCount; i += 1) { 33181 slideIndex = i; 33182 $(_.$slides[slideIndex]).clone(true).attr('id', '') 33183 .attr('data-slick-index', slideIndex + _.slideCount) 33184 .appendTo(_.$slideTrack).addClass('slick-cloned'); 33185 } 33186 _.$slideTrack.find('.slick-cloned').find('[id]').each(function() { 33187 $(this).attr('id', ''); 33188 }); 33189 33190 } 33191 33192 } 33193 33194 }; 33195 33196 Slick.prototype.setPaused = function(paused) { 33197 33198 var _ = this; 33199 33200 if (_.options.autoplay === true && _.options.pauseOnHover === true) { 33201 _.paused = paused; 33202 if (!paused) { 33203 _.autoPlay(); 33204 } else { 33205 _.autoPlayClear(); 33206 } 33207 } 33208 }; 33209 33210 Slick.prototype.selectHandler = function(event) { 33211 33212 var _ = this; 33213 33214 var targetElement = 33215 $(event.target).is('.slick-slide') ? 33216 $(event.target) : 33217 $(event.target).parents('.slick-slide'); 33218 33219 var index = parseInt(targetElement.attr('data-slick-index')); 33220 33221 if (!index) index = 0; 33222 33223 if (_.slideCount <= _.options.slidesToShow) { 33224 33225 _.setSlideClasses(index); 33226 _.asNavFor(index); 33227 return; 33228 33229 } 33230 33231 _.slideHandler(index); 33232 33233 }; 33234 33235 Slick.prototype.slideHandler = function(index, sync, dontAnimate) { 33236 33237 var targetSlide, animSlide, oldSlide, slideLeft, targetLeft = null, 33238 _ = this; 33239 33240 sync = sync || false; 33241 33242 if (_.animating === true && _.options.waitForAnimate === true) { 33243 return; 33244 } 33245 33246 if (_.options.fade === true && _.currentSlide === index) { 33247 return; 33248 } 33249 33250 if (_.slideCount <= _.options.slidesToShow) { 33251 return; 33252 } 33253 33254 if (sync === false) { 33255 _.asNavFor(index); 33256 } 33257 33258 targetSlide = index; 33259 targetLeft = _.getLeft(targetSlide); 33260 slideLeft = _.getLeft(_.currentSlide); 33261 33262 _.currentLeft = _.swipeLeft === null ? slideLeft : _.swipeLeft; 33263 33264 if (_.options.infinite === false && _.options.centerMode === false && (index < 0 || index > _.getDotCount() * _.options.slidesToScroll)) { 33265 if (_.options.fade === false) { 33266 targetSlide = _.currentSlide; 33267 if (dontAnimate !== true) { 33268 _.animateSlide(slideLeft, function() { 33269 _.postSlide(targetSlide); 33270 }); 33271 } else { 33272 _.postSlide(targetSlide); 33273 } 33274 } 33275 return; 33276 } else if (_.options.infinite === false && _.options.centerMode === true && (index < 0 || index > (_.slideCount - _.options.slidesToScroll))) { 33277 if (_.options.fade === false) { 33278 targetSlide = _.currentSlide; 33279 if (dontAnimate !== true) { 33280 _.animateSlide(slideLeft, function() { 33281 _.postSlide(targetSlide); 33282 }); 33283 } else { 33284 _.postSlide(targetSlide); 33285 } 33286 } 33287 return; 33288 } 33289 33290 if (_.options.autoplay === true) { 33291 clearInterval(_.autoPlayTimer); 33292 } 33293 33294 if (targetSlide < 0) { 33295 if (_.slideCount % _.options.slidesToScroll !== 0) { 33296 animSlide = _.slideCount - (_.slideCount % _.options.slidesToScroll); 33297 } else { 33298 animSlide = _.slideCount + targetSlide; 33299 } 33300 } else if (targetSlide >= _.slideCount) { 33301 if (_.slideCount % _.options.slidesToScroll !== 0) { 33302 animSlide = 0; 33303 } else { 33304 animSlide = targetSlide - _.slideCount; 33305 } 33306 } else { 33307 animSlide = targetSlide; 33308 } 33309 33310 _.animating = true; 33311 33312 _.$slider.trigger('beforeChange', [_, _.currentSlide, animSlide]); 33313 33314 oldSlide = _.currentSlide; 33315 _.currentSlide = animSlide; 33316 33317 _.setSlideClasses(_.currentSlide); 33318 33319 _.updateDots(); 33320 _.updateArrows(); 33321 33322 if (_.options.fade === true) { 33323 if (dontAnimate !== true) { 33324 33325 _.fadeSlideOut(oldSlide); 33326 33327 _.fadeSlide(animSlide, function() { 33328 _.postSlide(animSlide); 33329 }); 33330 33331 } else { 33332 _.postSlide(animSlide); 33333 } 33334 _.animateHeight(); 33335 return; 33336 } 33337 33338 if (dontAnimate !== true) { 33339 _.animateSlide(targetLeft, function() { 33340 _.postSlide(animSlide); 33341 }); 33342 } else { 33343 _.postSlide(animSlide); 33344 } 33345 33346 }; 33347 33348 Slick.prototype.startLoad = function() { 33349 33350 var _ = this; 33351 33352 if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) { 33353 33354 _.$prevArrow.hide(); 33355 _.$nextArrow.hide(); 33356 33357 } 33358 33359 if (_.options.dots === true && _.slideCount > _.options.slidesToShow) { 33360 33361 _.$dots.hide(); 33362 33363 } 33364 33365 _.$slider.addClass('slick-loading'); 33366 33367 }; 33368 33369 Slick.prototype.swipeDirection = function() { 33370 33371 var xDist, yDist, r, swipeAngle, _ = this; 33372 33373 xDist = _.touchObject.startX - _.touchObject.curX; 33374 yDist = _.touchObject.startY - _.touchObject.curY; 33375 r = Math.atan2(yDist, xDist); 33376 33377 swipeAngle = Math.round(r * 180 / Math.PI); 33378 if (swipeAngle < 0) { 33379 swipeAngle = 360 - Math.abs(swipeAngle); 33380 } 33381 33382 if ((swipeAngle <= 45) && (swipeAngle >= 0)) { 33383 return (_.options.rtl === false ? 'left' : 'right'); 33384 } 33385 if ((swipeAngle <= 360) && (swipeAngle >= 315)) { 33386 return (_.options.rtl === false ? 'left' : 'right'); 33387 } 33388 if ((swipeAngle >= 135) && (swipeAngle <= 225)) { 33389 return (_.options.rtl === false ? 'right' : 'left'); 33390 } 33391 if (_.options.verticalSwiping === true) { 33392 if ((swipeAngle >= 35) && (swipeAngle <= 135)) { 33393 return 'left'; 33394 } else { 33395 return 'right'; 33396 } 33397 } 33398 33399 return 'vertical'; 33400 33401 }; 33402 33403 Slick.prototype.swipeEnd = function(event) { 33404 33405 var _ = this, 33406 slideCount; 33407 33408 _.dragging = false; 33409 33410 _.shouldClick = (_.touchObject.swipeLength > 10) ? false : true; 33411 33412 if (_.touchObject.curX === undefined) { 33413 return false; 33414 } 33415 33416 if (_.touchObject.edgeHit === true) { 33417 _.$slider.trigger('edge', [_, _.swipeDirection()]); 33418 } 33419 33420 if (_.touchObject.swipeLength >= _.touchObject.minSwipe) { 33421 33422 switch (_.swipeDirection()) { 33423 case 'left': 33424 slideCount = _.options.swipeToSlide ? _.checkNavigable(_.currentSlide + _.getSlideCount()) : _.currentSlide + _.getSlideCount(); 33425 _.slideHandler(slideCount); 33426 _.currentDirection = 0; 33427 _.touchObject = {}; 33428 _.$slider.trigger('swipe', [_, 'left']); 33429 break; 33430 33431 case 'right': 33432 slideCount = _.options.swipeToSlide ? _.checkNavigable(_.currentSlide - _.getSlideCount()) : _.currentSlide - _.getSlideCount(); 33433 _.slideHandler(slideCount); 33434 _.currentDirection = 1; 33435 _.touchObject = {}; 33436 _.$slider.trigger('swipe', [_, 'right']); 33437 break; 33438 } 33439 } else { 33440 if (_.touchObject.startX !== _.touchObject.curX) { 33441 _.slideHandler(_.currentSlide); 33442 _.touchObject = {}; 33443 } 33444 } 33445 33446 }; 33447 33448 Slick.prototype.swipeHandler = function(event) { 33449 33450 var _ = this; 33451 33452 if ((_.options.swipe === false) || ('ontouchend' in document && _.options.swipe === false)) { 33453 return; 33454 } else if (_.options.draggable === false && event.type.indexOf('mouse') !== -1) { 33455 return; 33456 } 33457 33458 _.touchObject.fingerCount = event.originalEvent && event.originalEvent.touches !== undefined ? 33459 event.originalEvent.touches.length : 1; 33460 33461 _.touchObject.minSwipe = _.listWidth / _.options 33462 .touchThreshold; 33463 33464 if (_.options.verticalSwiping === true) { 33465 _.touchObject.minSwipe = _.listHeight / _.options 33466 .touchThreshold; 33467 } 33468 33469 switch (event.data.action) { 33470 33471 case 'start': 33472 _.swipeStart(event); 33473 break; 33474 33475 case 'move': 33476 _.swipeMove(event); 33477 break; 33478 33479 case 'end': 33480 _.swipeEnd(event); 33481 break; 33482 33483 } 33484 33485 }; 33486 33487 Slick.prototype.swipeMove = function(event) { 33488 33489 var _ = this, 33490 edgeWasHit = false, 33491 curLeft, swipeDirection, swipeLength, positionOffset, touches; 33492 33493 touches = event.originalEvent !== undefined ? event.originalEvent.touches : null; 33494 33495 if (!_.dragging || touches && touches.length !== 1) { 33496 return false; 33497 } 33498 33499 curLeft = _.getLeft(_.currentSlide); 33500 33501 _.touchObject.curX = touches !== undefined ? touches[0].pageX : event.clientX; 33502 _.touchObject.curY = touches !== undefined ? touches[0].pageY : event.clientY; 33503 33504 _.touchObject.swipeLength = Math.round(Math.sqrt( 33505 Math.pow(_.touchObject.curX - _.touchObject.startX, 2))); 33506 33507 if (_.options.verticalSwiping === true) { 33508 _.touchObject.swipeLength = Math.round(Math.sqrt( 33509 Math.pow(_.touchObject.curY - _.touchObject.startY, 2))); 33510 } 33511 33512 swipeDirection = _.swipeDirection(); 33513 33514 if (swipeDirection === 'vertical') { 33515 return; 33516 } 33517 33518 if (event.originalEvent !== undefined && _.touchObject.swipeLength > 4) { 33519 event.preventDefault(); 33520 } 33521 33522 positionOffset = (_.options.rtl === false ? 1 : -1) * (_.touchObject.curX > _.touchObject.startX ? 1 : -1); 33523 if (_.options.verticalSwiping === true) { 33524 positionOffset = _.touchObject.curY > _.touchObject.startY ? 1 : -1; 33525 } 33526 33527 33528 swipeLength = _.touchObject.swipeLength; 33529 33530 _.touchObject.edgeHit = false; 33531 33532 if (_.options.infinite === false) { 33533 if ((_.currentSlide === 0 && swipeDirection === 'right') || (_.currentSlide >= _.getDotCount() && swipeDirection === 'left')) { 33534 swipeLength = _.touchObject.swipeLength * _.options.edgeFriction; 33535 _.touchObject.edgeHit = true; 33536 } 33537 } 33538 33539 if (_.options.vertical === false) { 33540 _.swipeLeft = curLeft + swipeLength * positionOffset; 33541 } else { 33542 _.swipeLeft = curLeft + (swipeLength * (_.$list.height() / _.listWidth)) * positionOffset; 33543 } 33544 if (_.options.verticalSwiping === true) { 33545 _.swipeLeft = curLeft + swipeLength * positionOffset; 33546 } 33547 33548 if (_.options.fade === true || _.options.touchMove === false) { 33549 return false; 33550 } 33551 33552 if (_.animating === true) { 33553 _.swipeLeft = null; 33554 return false; 33555 } 33556 33557 _.setCSS(_.swipeLeft); 33558 33559 }; 33560 33561 Slick.prototype.swipeStart = function(event) { 33562 33563 var _ = this, 33564 touches; 33565 33566 if (_.touchObject.fingerCount !== 1 || _.slideCount <= _.options.slidesToShow) { 33567 _.touchObject = {}; 33568 return false; 33569 } 33570 33571 if (event.originalEvent !== undefined && event.originalEvent.touches !== undefined) { 33572 touches = event.originalEvent.touches[0]; 33573 } 33574 33575 _.touchObject.startX = _.touchObject.curX = touches !== undefined ? touches.pageX : event.clientX; 33576 _.touchObject.startY = _.touchObject.curY = touches !== undefined ? touches.pageY : event.clientY; 33577 33578 _.dragging = true; 33579 33580 }; 33581 33582 Slick.prototype.unfilterSlides = Slick.prototype.slickUnfilter = function() { 33583 33584 var _ = this; 33585 33586 if (_.$slidesCache !== null) { 33587 33588 _.unload(); 33589 33590 _.$slideTrack.children(this.options.slide).detach(); 33591 33592 _.$slidesCache.appendTo(_.$slideTrack); 33593 33594 _.reinit(); 33595 33596 } 33597 33598 }; 33599 33600 Slick.prototype.unload = function() { 33601 33602 var _ = this; 33603 33604 $('.slick-cloned', _.$slider).remove(); 33605 33606 if (_.$dots) { 33607 _.$dots.remove(); 33608 } 33609 33610 if (_.$prevArrow && _.htmlExpr.test(_.options.prevArrow)) { 33611 _.$prevArrow.remove(); 33612 } 33613 33614 if (_.$nextArrow && _.htmlExpr.test(_.options.nextArrow)) { 33615 _.$nextArrow.remove(); 33616 } 33617 33618 _.$slides 33619 .removeClass('slick-slide slick-active slick-visible slick-current') 33620 .attr('aria-hidden', 'true') 33621 .css('width', ''); 33622 33623 }; 33624
33625 Slick.prototype.unslick = function(fromBreakpoint) { 33626 33627 var _ = this; 33628 _.$slider.trigger('unslick', [_, fromBreakpoint]); 33629 _.destroy(); 33630 33631 }; 33632 33633 Slick.prototype.updateArrows = function() { 33634 33635 var _ = this, 33636 centerOffset; 33637 33638 centerOffset = Math.floor(_.options.slidesToShow / 2); 33639 33640 if ( _.options.arrows === true && 33641 _.slideCount > _.options.slidesToShow && 33642 !_.options.infinite ) { 33643 33644 _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); 33645 _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); 33646 33647 if (_.currentSlide === 0) { 33648 33649 _.$prevArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); 33650 _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); 33651 33652 } else if (_.currentSlide >= _.slideCount - _.options.slidesToShow && _.options.centerMode === false) { 33653 33654 _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); 33655 _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); 33656 33657 } else if (_.currentSlide >= _.slideCount - 1 && _.options.centerMode === true) { 33658 33659 _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true'); 33660 _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false'); 33661 33662 } 33663 33664 } 33665 33666 }; 33667 33668 Slick.prototype.updateDots = function() { 33669 33670 var _ = this; 33671 33672 if (_.$dots !== null) { 33673 33674 _.$dots 33675 .find('li') 33676 .removeClass('slick-active') 33677 .attr('aria-hidden', 'true'); 33678 33679 _.$dots 33680 .find('li') 33681 .eq(Math.floor(_.currentSlide / _.options.slidesToScroll)) 33682 .addClass('slick-active') 33683 .attr('aria-hidden', 'false'); 33684 33685 } 33686 33687 }; 33688 33689 Slick.prototype.visibility = function() { 33690 33691 var _ = this; 33692 33693 if (document[_.hidden]) { 33694 _.paused = true; 33695 _.autoPlayClear(); 33696 } else { 33697 if (_.options.autoplay === true) { 33698 _.paused = false; 33699 _.autoPlay(); 33700 } 33701 } 33702 33703 }; 33704 Slick.prototype.initADA = function() { 33705 var _ = this; 33706 _.$slides.add(_.$slideTrack.find('.slick-cloned')).attr({ 33707 'aria-hidden': 'true', 33708 'tabindex': '-1' 33709 }).find('a, input, button, select').attr({ 33710 'tabindex': '-1' 33711 }); 33712 33713 _.$slideTrack.attr('role', 'listbox'); 33714 33715 _.$slides.not(_.$slideTrack.find('.slick-cloned')).each(function(i) { 33716 $(this).attr({ 33717 'role': 'option', 33718 'aria-describedby': 'slick-slide' + _.instanceUid + i + '' 33719 }); 33720 }); 33721 33722 if (_.$dots !== null) { 33723 _.$dots.attr('role', 'tablist').find('li').each(function(i) { 33724 $(this).attr({ 33725 'role': 'presentation', 33726 'aria-selected': 'false', 33727 'aria-controls': 'navigation' + _.instanceUid + i + '', 33728 'id': 'slick-slide' + _.instanceUid + i + '' 33729 }); 33730 }) 33731 .first().attr('aria-selected', 'true').end() 33732 .find('button').attr('role', 'button').end() 33733 .closest('div').attr('role', 'toolbar'); 33734 } 33735 _.activateADA(); 33736 33737 }; 33738 33739 Slick.prototype.activateADA = function() { 33740 var _ = this; 33741 33742 _.$slideTrack.find('.slick-active').attr({ 33743 'aria-hidden': 'false' 33744 }).find('a, input, button, select').attr({ 33745 'tabindex': '0' 33746 }); 33747 33748 }; 33749 33750 Slick.prototype.focusHandler = function() { 33751 var _ = this; 33752 _.$slider.on('focus.slick blur.slick', '*', function(event) { 33753 event.stopImmediatePropagation(); 33754 var sf = $(this); 33755 setTimeout(function() { 33756 if (_.isPlay) { 33757 if (sf.is(':focus')) { 33758 _.autoPlayClear(); 33759 _.paused = true; 33760 } else { 33761 _.paused = false; 33762 _.autoPlay(); 33763 } 33764 } 33765 }, 0); 33766 }); 33767 }; 33768 33769 $.fn.slick = function() { 33770 var _ = this, 33771 opt = arguments[0], 33772 args = Array.prototype.slice.call(arguments, 1), 33773 l = _.length, 33774 i, 33775 ret; 33776 for (i = 0; i < l; i++) { 33777 if (typeof opt == 'object' || typeof opt == 'undefined') 33778 _[i].slick = new Slick(_[i], opt); 33779 else 33780 ret = _[i].slick[opt].apply(_[i].slick, args); 33781 if (typeof ret != 'undefined') return ret; 33782 } 33783 return _; 33784 }; 33785 33786})); 33787 33788// jQuery RoyalSlider plugin. Copyright Dmitry Semenov, http://dim
33788semenov.com 33789// jquery.royalslider v9.5.4 33790(function(n){function u(b,f){var c,a=this,e=window.navigator,g=e.userAgent.toLowerCase();a.uid=n.rsModules.uid++;a.ns=".rs"+a.uid;var d=document.createElement("div").style,h=["webkit","Moz","ms","O"],k="",l=0,r;for(c=0;c<h.length;c++)r=h[c],!k&&r+"Transform"in d&&(k=r),r=r.toLowerCase(),window.requestAnimationFrame||(window.requestAnimationFrame=window[r+"RequestAnimationFrame"],window.cancelAnimationFrame=window[r+"CancelAnimationFrame"]||window[r+"CancelRequestAnimationFrame"]);window.requestAnimationFrame|| 33791(window.requestAnimationFrame=function(a,b){var c=(new Date).getTime(),d=Math.max(0,16-(c-l)),e=window.setTimeout(function(){a(c+d)},d);l=c+d;return e});window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)});a.isIPAD=g.match(/(ipad)/);a.isIOS=a.isIPAD||g.match(/(iphone|ipod)/);c=function(a){a=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||0>a.indexOf("compatible")&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)|| 33792[];return{browser:a[1]||"",version:a[2]||"0"}}(g);h={};c.browser&&(h[c.browser]=!0,h.version=c.version);h.chrome&&(h.webkit=!0);a._a=h;a.isAndroid=-1<g.indexOf("android");a.slider=n(b);a.ev=n(a);a._b=n(document);a.st=n.extend({},n.fn.royalSlider.defaults,f);a._c=a.st.transitionSpeed;a._d=0;!a.st.allowCSS3||h.webkit&&!a.st.allowCSS3OnWebkit||(c=k+(k?"T":"t"),a._e=c+"ransform"in d&&c+"ransition"in d,a._e&&(a._f=k+(k?"P":"p")+"erspective"in d));k=k.toLowerCase();a._g="-"+k+"-";a._h="vertical"===a.st.slidesOrientation? 33793!1:!0;a._i=a._h?"left":"top";a._j=a._h?"width":"height";a._k=-1;a._l="fade"===a.st.transitionType?!1:!0;a._l||(a.st.sliderDrag=!1,a._m=10);a._n="z-index:0; display:none; opacity:0;";a._o=0;a._p=0;a._q=0;n.each(n.rsModules,function(b,c){"uid"!==b&&c.call(a)});a.slides=[];a._r=0;(a.st.slides?n(a.st.slides):a.slider.children().detach()).each(function(){a._s(this,!0)});a.st.randomizeSlides&&a.slides.sort(function(){return 0.5-Math.random()});a.numSlides=a.slides.length;a._t();a.st.startSlideId?a.st.startSlideId> 33794a.numSlides-1&&(a.st.startSlideId=a.numSlides-1):a.st.startSlideId=0;a._o=a.staticSlideId=a.currSlideId=a._u=a.st.startSlideId;a.currSlide=a.slides[a.currSlideId];a._v=0;a.pointerMultitouch=!1;a.slider.addClass((a._h?"rsHor":"rsVer")+(a._l?"":" rsFade"));d='<div class="rsOverflow"><div class="rsContainer">';a.slidesSpacing=a.st.slidesSpacing;a._w=(a._h?a.slider.width():a.slider.height())+a.st.slidesSpacing;a._x=Boolean(0<a._y);1>=a.numSlides&&(a._z=!1);a._a1=a._z&&a._l?2===a.numSlides?1:2:0;a._b1= 337956>a.numSlides?a.numSlides:6;a._c1=0;a._d1=0;a.slidesJQ=[];for(c=0;c<a.numSlides;c++)a.slidesJQ.push(n('<div style="'+(a._l?"":c!==a.currSlideId?a._n:"z-index:0;")+'" class="rsSlide "></div>'));a._e1=d=n(d+"</div></div>");var m=a.ns,k=function(b,c,d,e,f){a._j1=b+c+m;a._k1=b+d+m;a._l1=b+e+m;f&&(a._m1=b+f+m)};c=e.pointerEnabled;a.pointerEnabled=c||e.msPointerEnabled;a.pointerEnabled?(a.hasTouch=!1,a._n1=0.2,a.pointerMultitouch=Boolean(1<e[(c?"m":"msM")+"axTouchPoints"]),c?k("pointer","down","move","up", 33796"cancel"):k("MSPointer","Down","Move","Up","Cancel")):(a.isIOS?a._j1=a._k1=a._l1=a._m1="":k("mouse","down","move","up"),"ontouchstart"in window||"createTouch"in document?(a.hasTouch=!0,a._j1+=" touchstart"+m,a._k1+=" touchmove"+m,a._l1+=" touchend"+m,a._m1+=" touchcancel"+m,a._n1=0.5,a.st.sliderTouch&&(a._f1=!0)):(a.hasTouch=!1,a._n1=0.2));a.st.sliderDrag&&(a._f1=!0,h.msie||h.opera?a._g1=a._h1="move":h.mozilla?(a._g1="-moz-grab",a._h1="-moz-grabbing"):h.webkit&&-1!=e.platform.indexOf("Mac")&&(a._g1= 33797"-webkit-grab",a._h1="-webkit-grabbing"),a._i1());a.slider.html(d);a._o1=a.st.controlsInside?a._e1:a.slider;a._p1=a._e1.children(".rsContainer");a.pointerEnabled&&a._p1.css((c?"":"-ms-")+"touch-action",a._h?"pan-y":"pan-x");a._q1=n('<div class="rsPreloader"></div>');e=a._p1.children(".rsSlide");a._r1=a.slidesJQ[a.currSlideId];a._s1=0;a._e?(a._t1="transition-property",a._u1="transition-duration",a._v1="transition-timing-function",a._w1=a._x1=a._g+"transform",a._f?(h.webkit&&!h.chrome&&a.slider.addClass("rsWebkit3d"), 33798a._y1="translate3d(",a._z1="px, ",a._a2="px, 0px)"):(a._y1="translate(",a._z1="px, ",a._a2="px)"),a._l?a._p1[a._g+a._t1]=a._g+"transform":(h={},h[a._g+a._t1]="opacity",h[a._g+a._u1]=a.st.transitionSpeed+"ms",h[a._g+a._v1]=a.st.css3easeInOut,e.css(h))):(a._x1="left",a._w1="top");var p;n(window).on("resize"+a.ns,function(){p&&clearTimeout(p);p=setTimeout(function(){a.updateSliderSize()},50)});a.ev.trigger("rsAfterPropsSetup");a.updateSliderSize();a.st.keyboardNavEnabled&&a._b2();a.st.arrowsNavHideOnTouch&& 33799(a.hasTouch||a.pointerMultitouch)&&(a.st.arrowsNav=!1);a.st.arrowsNav&&(e=a._o1,n('<div class="rsArrow rsArrowLeft"><div class="rsArrowIcn"></div></div><div class="rsArrow rsArrowRight"><div class="rsArrowIcn"></div></div>').appendTo(e),a._c2=e.children(".rsArrowLeft").click(function(b){b.preventDefault();a.prev()}),a._d2=e.children(".rsArrowRight").click(function(b){b.preventDefault();a.next()}),a.st.arrowsNavAutoHide&&!a.hasTouch&&(a._c2.addClass("rsHidden"),a._d2.addClass("rsHidden"),e.one("mousemove.arrowshover", 33800function(){a._c2.removeClass("rsHidden");a._d2.removeClass("rsHidden")}),e.hover(function(){a._e2||(a._c2.removeClass("rsHidden"),a._d2.removeClass("rsHidden"))},function(){a._e2||(a._c2.addClass("rsHidden"),a._d2.addClass("rsHidden"))})),a.ev.on("rsOnUpdateNav",function(){a._f2()}
33800),a._f2());if(a._f1)a._p1.on(a._j1,function(b){a._g2(b)});else a.dragSuccess=!1;var q=["rsPlayBtnIcon","rsPlayBtn","rsCloseVideoBtn","rsCloseVideoIcn"];a._p1.click(function(b){if(!a.dragSuccess){var c=n(b.target).attr("class"); 33801if(-1!==n.inArray(c,q)&&a.toggleVideo())return!1;if(a.st.navigateByClick&&!a._h2){if(n(b.target).closest(".rsNoDrag",a._r1).length)return!0;a._i2(b)}a.ev.trigger("rsSlideClick",b)}}).on("click.rs","a",function(b){if(a.dragSuccess)return!1;a._h2=!0;setTimeout(function(){a._h2=!1},3)});a.ev.trigger("rsAfterInit")}n.rsModules||(n.rsModules={uid:0});u.prototype={constructor:u,_i2:function(b){b=b[this._h?"pageX":"pageY"]-this._j2;b>=this._q?this.next():0>b&&this.prev()},_t:function(){var b;b=this.st.numImagesToPreload; 33802if(this._z=this.st.loop)2===this.numSlides?(this._z=!1,this.st.loopRewind=!0):2>this.numSlides&&(this.st.loopRewind=this._z=!1);this._z&&0<b&&(4>=this.numSlides?b=1:this.st.numImagesToPreload>(this.numSlides-1)/2&&(b=Math.floor((this.numSlides-1)/2)));this._y=b},_s:function(b,f){function c(b,c){c?g.images.push(b.attr(c)):g.images.push(b.text());if(h){h=!1;g.caption="src"===c?b.attr("alt"):b.contents();g.image=g.images[0];g.videoURL=b.attr("data-rsVideo");var d=b.attr("data-rsw"),e=b.attr("data-rsh"); 33803"undefined"!==typeof d&&!1!==d&&"undefined"!==typeof e&&!1!==e?(g.iW=parseInt(d,10),g.iH=parseInt(e,10)):a.st.imgWidth&&a.st.imgHeight&&(g.iW=a.st.imgWidth,g.iH=a.st.imgHeight)}}var a=this,e,g={},d,h=!0;b=n(b);a._k2=b;a.ev.trigger("rsBeforeParseNode",[b,g]);if(!g.stopParsing)return b=a._k2,g.id=a._r,g.contentAdded=!1,a._r++,g.images=[],g.isBig=!1,g.hasCover||(b.hasClass("rsImg")?(d=b,e=!0):(d=b.find(".rsImg"),d.length&&(e=!0)),e?(g.bigImage=d.eq(0).attr("data-rsBigImg"),d.each(function(){var a=n(this); 33804a.is("a")?c(a,"href"):a.is("img")?c(a,"src"):c(a)})):b.is("img")&&(b.addClass("rsImg rsMainSlideImage"),c(b,"src"))),d=b.find(".rsCaption"),d.length&&(g.caption=d.remove()),g.content=b,a.ev.trigger("rsAfterParseNode",[b,g]),f&&a.slides.push(g),0===g.images.length&&(g.isLoaded=!0,g.isRendered=!1,g.isLoading=!1,g.images=null),g},_b2:function(){var b=this,f,c,a=function(a){37===a?b.prev():39===a&&b.next()};b._b.on("keydown"+b.ns,function(e){b._l2||(c=e.keyCode,37!==c&&39!==c||f||(a(c),f=setInterval(function(){a(c)}, 33805700)))}).on("keyup"+b.ns,function(a){f&&(clearInterval(f),f=null)})},goTo:function(b,f){b!==this.currSlideId&&this._m2(b,this.st.transitionSpeed,!0,!f)},destroy:function(b){this.ev.trigger("rsBeforeDestroy");this._b.off("keydown"+this.ns+" keyup"+this.ns+" "+this._k1+" "+this._l1);this._p1.off(this._j1+" click");this.slider.data("royalSlider",null);n.removeData(this.slider,"royalSlider");n(window).off("resize"+this.ns);this.loadingTimeout&&clearTimeout(this.loadingTimeout);b&&this.slider.remove(); 33806this.ev=this.slider=this.slides=null},_n2:function(b,f){function c(c,f,g){c.isAdded?(a(f,c),e(f,c)):(g||(g=d.slidesJQ[f]),c.holder?g=c.holder:(g=d.slidesJQ[f]=n(g),c.holder=g),c.appendOnLoaded=!1,e(f,c,g),a(f,c),d._p2(c,g,b),c.isAdded=!0)}function a(a,c){c.contentAdded||(d.setItemHtml(c,b),b||(c.contentAdded=!0))}function e(a,b,c){d._l&&(c||(c=d.slidesJQ[a]),c.css(d._i,(a+d._d1+p)*d._w))}function g(a){if(l){if(a>r-1)return g(a-r);if(0>a)return g(r+a)}return a}var d=this,h,k,l=d._z,r=d.numSlides;if(!isNaN(f))return g(f); 33807var m=d.currSlideId,p,q=b?Math.abs(d._o2-d.currSlideId)>=d.numSlides-1?0:1:d._y,s=Math.min(2,q),v=!1,u=!1,t;for(k=m;k<m+1+s;k++)if(t=g(k),(h=d.slides[t])&&(!h.isAdded||!h.positionSet)){v=!0;break}for(k=m-1;k>m-1-s;k--)if(t=g(k),(h=d.slides[t])&&(!h.isAdded||!h.positionSet)){u=!0;break}if(v)for(k=m;k<m+q+1;k++)t=g(k),p=Math.floor((d._u-(m-k))/d.numSlides)*d.numSlides,(h=d.slides[t])&&c(h,t);if(u)for(k=m-1;k>m-1-q;k--)t=g(k),p=Math.floor((d._u-(m-k))/r)*r,(h=d.slides[t])&&c(h,t);if(!b)for(s=g(m-q), 33808m=g(m+q),q=s>m?0:s,k=0;k<r;k++)s>m&&k>s-1||!(k<q||k>m)||(h=d.slides[k])&&h.holder&&(h.holder.detach(),h.isAdded=!1)},setItemHtml:function(b,f){var c=this,a=function(){if(!b.images)b.isRendered=!0,b.isLoaded=!0,b.isLoading=!1,d(!0);else if(!b.isLoading){var a,f;b.content.hasClass("rsImg")?(a=b.content,f=!0):a=b.content.find(".rsImg:not(img)");a&&!a.is("img")&&a.each(function(){var a=n(this),c='<img class="rsImg" src="'+(a.is("a")?a.attr("href"):a.text())+'" />';f?b.content=n(c):a.replaceWith(c)}); 33809a=f?b.content:b.content.find("img.rsImg");k();a.eq(0).addClass("rsMainSlideImage");b.iW&&b.iH&&(b.isLoaded||c._q2(b),d());b.isLoading=!0;if(b.isBig)n("<img />").on("load.rs error.rs",function(a){n(this).off("load.rs error.rs");e([this],!0)}).attr("src",b.image);else{b.loaded=[];b.numStartedLoad=0;a=function(a){n(this).off("load.rs error.rs");b.loaded.push(this);b.loaded.length===b.numStartedLoad&&e(b.loaded,!1)};for(var g=0;g<b.images.length;g++){var h=n("<img />");b.numStartedLoad++;h.on("load.rs error.rs", 33810a).attr("src",b.images[g])}}}},e=function(a,c){if(a.length){var d=a[0];if(c!==b.isBig)(d=b.holder.children())&&1<d.length&&l();else if(b.iW&&b.iH)g();else if(b.iW=d.width,b.iH=d.height,b.iW&&b.iH)g();else{var e=new Image;
33810e.onload=function(){e.width?(b.iW=e.width,b.iH=e.height,g()):setTimeout(function(){e.width&&(b.iW=e.width,b.iH=e.height);g()},1E3)};e.src=d.src}}else g()},g=function(){b.isLoaded=!0;b.isLoading=!1;d();l();h()},d=function(){if(!b.isAppended&&c.ev){var a=c.st.visibleNearby,d=b.id-c._o; 33811f||b.appendOnLoaded||!c.st.fadeinLoadedSlide||0!==d&&(!(a||c._r2||c._l2)||-1!==d&&1!==d)||(a={visibility:"visible",opacity:0},a[c._g+"transition"]="opacity 400ms ease-in-out",b.content.css(a),setTimeout(function(){b.content.css("opacity",1)},16));b.holder.find(".rsPreloader").length?b.holder.append(b.content):b.holder.html(b.content);b.isAppended=!0;b.isLoaded&&(c._q2(b),h());b.sizeReady||(b.sizeReady=!0,setTimeout(function(){c.ev.trigger("rsMaybeSizeReady",b)},100))}},h=function(){!b.loadedTriggered&& 33812c.ev&&(b.isLoaded=b.loadedTriggered=!0,b.holder.trigger("rsAfterContentSet"),c.ev.trigger("rsAfterContentSet",b))},k=function(){c.st.usePreloader&&b.holder.html(c._q1.clone())},l=function(a){c.st.usePreloader&&(a=b.holder.find(".rsPreloader"),a.length&&a.remove())};b.isLoaded?d():f?!c._l&&b.images&&b.iW&&b.iH?a():(b.holder.isWaiting=!0,k(),b.holder.slideId=-99):a()},_p2:function(b,f,c){this._p1.append(b.holder);b.appendOnLoaded=!1},_g2:function(b,f){var c=this,a,e="touchstart"===b.type;c._s2=e;c.ev.trigger("rsDragStart"); 33813if(n(b.target).closest(".rsNoDrag",c._r1).length)return c.dragSuccess=!1,!0;!f&&c._r2&&(c._t2=!0,c._u2());c.dragSuccess=!1;if(c._l2)e&&(c._v2=!0);else{e&&(c._v2=!1);c._w2();if(e){var g=b.originalEvent.touches;if(g&&0<g.length)a=g[0],1<g.length&&(c._v2=!0);else return}else b.preventDefault(),a=b,c.pointerEnabled&&(a=a.originalEvent);c._l2=!0;c._b.on(c._k1,function(a){c._x2(a,f)}).on(c._l1,function(a){c._y2(a,f)});c._z2="";c._a3=!1;c._b3=a.pageX;c._c3=a.pageY;c._d3=c._v=(f?c._e3:c._h)?a.pageX:a.pageY; 33814c._f3=0;c._g3=0;c._h3=f?c._i3:c._p;c._j3=(new Date).getTime();if(e)c._e1.on(c._m1,function(a){c._y2(a,f)})}},_k3:function(b,f){if(this._l3){var c=this._m3,a=b.pageX-this._b3,e=b.pageY-this._c3,g=this._h3+a,d=this._h3+e,h=f?this._e3:this._h,g=h?g:d,d=this._z2;this._a3=!0;this._b3=b.pageX;this._c3=b.pageY;"x"===d&&0!==a?this._f3=0<a?1:-1:"y"===d&&0!==e&&(this._g3=0<e?1:-1);d=h?this._b3:this._c3;a=h?a:e;f?g>this._n3?g=this._h3+a*this._n1:g<this._o3&&(g=this._h3+a*this._n1):this._z||(0>=this.currSlideId&& 338150<d-this._d3&&(g=this._h3+a*this._n1),this.currSlideId>=this.numSlides-1&&0>d-this._d3&&(g=this._h3+a*this._n1));this._h3=g;200<c-this._j3&&(this._j3=c,this._v=d);f?this._q3(this._h3):this._l&&this._p3(this._h3)}},_x2:function(b,f){var c=this,a,e="touchmove"===b.type;if(!c._s2||e){if(e){if(c._r3)return;var g=b.originalEvent.touches;if(g){if(1<g.length)return;a=g[0]}else return}else a=b,c.pointerEnabled&&(a=a.originalEvent);c._a3||(c._e&&(f?c._s3:c._p1).css(c._g+c._u1,"0s"),function h(){c._l2&&(c._t3= 33816requestAnimationFrame(h),c._u3&&c._k3(c._u3,f))}());if(c._l3)b.preventDefault(),c._m3=(new Date).getTime(),c._u3=a;else if(g=f?c._e3:c._h,a=Math.abs(a.pageX-c._b3)-Math.abs(a.pageY-c._c3)-(g?-7:7),7<a){if(g)b.preventDefault(),c._z2="x";else if(e){c._v3(b);return}c._l3=!0}else if(-7>a){if(!g)b.preventDefault(),c._z2="y";else if(e){c._v3(b);return}c._l3=!0}}},_v3:function(b,f){this._r3=!0;this._a3=this._l2=!1;this._y2(b)},_y2:function(b,f){function c(a){return 100>a?100:500<a?500:a}function a(a,b){if(e._l|| 33817f)h=(-e._u-e._d1)*e._w,k=Math.abs(e._p-h),e._c=k/b,a&&(e._c+=250),e._c=c(e._c),e._x3(h,!1)}var e=this,g,d,h,k;g=-1<b.type.indexOf("touch");if(!e._s2||g)if(e._s2=!1,e.ev.trigger("rsDragRelease"),e._u3=null,e._l2=!1,e._r3=!1,e._l3=!1,e._m3=0,cancelAnimationFrame(e._t3),e._a3&&(f?e._q3(e._h3):e._l&&e._p3(e._h3)),e._b.off(e._k1).off(e._l1),g&&e._e1.off(e._m1),e._i1(),!e._a3&&!e._v2&&f&&e._w3){var l=n(b.target).closest(".rsNavItem");l.length&&e.goTo(l.index())}else{d=f?e._e3:e._h;if(!e._a3||"y"===e._z2&& 33818d||"x"===e._z2&&!d)if(!f&&e._t2){e._t2=!1;if(e.st.navigateByClick){e._i2(e.pointerEnabled?b.originalEvent:b);e.dragSuccess=!0;return}e.dragSuccess=!0}else{e._t2=!1;e.dragSuccess=!1;return}else e.dragSuccess=!0;e._t2=!1;e._z2="";var r=e.st.minSlideOffset;g=g?b.originalEvent.changedTouches[0]:e.pointerEnabled?b.originalEvent:b;var m=d?g.pageX:g.pageY,p=e._d3;g=e._v;var q=e.currSlideId,s=e.numSlides,v=d?e._f3:e._g3,u=e._z;Math.abs(m-p);g=m-g;d=(new Date).getTime()-e._j3;d=Math.abs(g)/d;if(0===v||1>= 33819s)a(!0,d);else{if(!u&&!f)if(0>=q){if(0<v){a(!0,d);return}}else if(q>=s-1&&0>v){a(!0,d);return}if(f){h=e._i3;if(h>e._n3)h=e._n3;else if(h<e._o3)h=e._o3;else{m=d*d/0.006;l=-e._i3;p=e._y3-e._z3+e._i3;0<g&&m>l?(l+=e._z3/(15/(m/d*0.003)),d=d*l/m,m=l):0>g&&m>p&&(p+=e._z3/(15/(m/d*0.003)),d=d*p/m,m=p);l=Math.max(Math.round(d/0.003),50);h+=m*(0>g?-1:1);if(h>e._n3){e._a4(h,l,!0,e._n3,200);return}if(h<e._o3){e._a4(h,l,!0,e._o3,200);return}}e._a4(h,l,!0)}else l=function(a){var b=Math.floor(a/e._w);a-b*e._w> 33820r&&b++;return b},p+r<m?0>v?a(!1,d):(l=l(m-p),e._m2(e.currSlideId-l,c(Math.abs(e._p-(-e._u-e._d1+l)*e._w)/d),!1,!0,!0)):p-r>m?0<v?a(!1,d):(l=l(p-m),e._m2(e.currSlideId+l,c(Math.abs(e._p-(-e._u-e._d1-l)*e._w)/d),!1,!0,!0)):a(!1,d)}}},_p3:function(b){b=this._p=b;this._e?this._p1.css(this._x1,this._y1+(this._h?b+this._z1+0:0+this._z1+b)+this._a2):this._p1.css(this._h?this._x1:this._w1,b)},updateSliderSize:function(b){var f,c;if(this.slider){if(this.st.autoScaleSlider){var a=this.st.autoScaleSliderWidth, 33821e=this.st.autoScaleSliderHeight;this.st.autoScaleHeight?(f=this.slider.width(),f!=this.width&&(this.slider.css("height",e/a*f),f=this.slider.width()),c=this.slider.height()):(c=this.slider.height(),c!=this.height&&(this.slider.css("width",a/e*c),c=this.slider.height()),f=this.slider.width())}else f=this.slider.width(),c=this.slider.height();if(b||f!=this.width||c!=this.height){this.width=f;this.height=c;this._b4=f;this._c4=c;this.ev.trigger("rsBeforeSizeSet");this.ev.trigger("rsAfterSizePropSet"); 33822this._e1.css({width:this._b4,height:this._c4});this._w=(this._h?this._b4:this._c4)+this.st.slidesSpacing;this._d4=this.st.imageScalePadding;
33822for(f=0;f<this.slides.length;f++)b=this.slides[f],b.positionSet=!1,b&&b.images&&b.isLoaded&&(b.isRendered=!1,this._q2(b));if(this._e4)for(f=0;f<this._e4.length;f++)b=this._e4[f],b.holder.css(this._i,(b.id+this._d1)*this._w);this._n2();this._l&&(this._e&&this._p1.css(this._g+"transition-duration","0s"),this._p3((-this._u-this._d1)*this._w));this.ev.trigger("rsOnUpdateNav")}this._j2= 33823this._e1.offset();this._j2=this._j2[this._i]}},appendSlide:function(b,f){var c=this._s(b);if(isNaN(f)||f>this.numSlides)f=this.numSlides;this.slides.splice(f,0,c);this.slidesJQ.splice(f,0,n('<div style="'+(this._l?"position:absolute;":this._n)+'" class="rsSlide"></div>'));f<=this.currSlideId&&this.currSlideId++;this.ev.trigger("rsOnAppendSlide",[c,f]);this._f4(f);f===this.currSlideId&&this.ev.trigger("rsAfterSlideChange")},removeSlide:function(b){var f=this.slides[b];f&&(f.holder&&f.holder.remove(), 33824b<this.currSlideId&&this.currSlideId--,this.slides.splice(b,1),this.slidesJQ.splice(b,1),this.ev.trigger("rsOnRemoveSlide",[b]),this._f4(b),b===this.currSlideId&&this.ev.trigger("rsAfterSlideChange"))},_f4:function(b){var f=this;b=f.numSlides;b=0>=f._u?0:Math.floor(f._u/b);f.numSlides=f.slides.length;0===f.numSlides?(f.currSlideId=f._d1=f._u=0,f.currSlide=f._g4=null):f._u=b*f.numSlides+f.currSlideId;for(b=0;b<f.numSlides;b++)f.slides[b].id=b;f.currSlide=f.slides[f.currSlideId];f._r1=f.slidesJQ[f.currSlideId]; 33825f.currSlideId>=f.numSlides?f.goTo(f.numSlides-1):0>f.currSlideId&&f.goTo(0);f._t();f._l&&f._p1.css(f._g+f._u1,"0ms");f._h4&&clearTimeout(f._h4);f._h4=setTimeout(function(){f._l&&f._p3((-f._u-f._d1)*f._w);f._n2();f._l||f._r1.css({display:"block",opacity:1})},14);f.ev.trigger("rsOnUpdateNav")},_i1:function(){this._f1&&this._l&&(this._g1?this._e1.css("cursor",this._g1):(this._e1.removeClass("grabbing-cursor"),this._e1.addClass("grab-cursor")))},_w2:function(){this._f1&&this._l&&(this._h1?this._e1.css("cursor", 33826this._h1):(this._e1.removeClass("grab-cursor"),this._e1.addClass("grabbing-cursor")))},next:function(b){this._m2("next",this.st.transitionSpeed,!0,!b)},prev:function(b){this._m2("prev",this.st.transitionSpeed,!0,!b)},_m2:function(b,f,c,a,e){var g=this,d,h,k;g.ev.trigger("rsBeforeMove",[b,a]);k="next"===b?g.currSlideId+1:"prev"===b?g.currSlideId-1:b=parseInt(b,10);if(!g._z){if(0>k){g._i4("left",!a);return}if(k>=g.numSlides){g._i4("right",!a);return}}g._r2&&(g._u2(!0),c=!1);h=k-g.currSlideId;k=g._o2= 33827g.currSlideId;var l=g.currSlideId+h;a=g._u;var n;g._z?(l=g._n2(!1,l),a+=h):a=l;g._o=l;g._g4=g.slidesJQ[g.currSlideId];g._u=a;g.currSlideId=g._o;g.currSlide=g.slides[g.currSlideId];g._r1=g.slidesJQ[g.currSlideId];var l=g.st.slidesDiff,m=Boolean(0<h);h=Math.abs(h);var p=Math.floor(k/g._y),q=Math.floor((k+(m?l:-l))/g._y),p=(m?Math.max(p,q):Math.min(p,q))*g._y+(m?g._y-1:0);p>g.numSlides-1?p=g.numSlides-1:0>p&&(p=0);k=m?p-k:k-p;k>g._y&&(k=g._y);if(h>k+l)for(g._d1+=(h-(k+l))*(m?-1:1),f*=1.4,k=0;k<g.numSlides;k++)g.slides[k].positionSet= 33828!1;g._c=f;g._n2(!0);e||(n=!0);d=(-a-g._d1)*g._w;n?setTimeout(function(){g._j4=!1;g._x3(d,b,!1,c);g.ev.trigger("rsOnUpdateNav")},0):(g._x3(d,b,!1,c),g.ev.trigger("rsOnUpdateNav"))},_f2:function(){this.st.arrowsNav&&(1>=this.numSlides?(this._c2.css("display","none"),this._d2.css("display","none")):(this._c2.css("display","block"),this._d2.css("display","block"),this._z||this.st.loopRewind||(0===this.currSlideId?this._c2.addClass("rsArrowDisabled"):this._c2.removeClass("rsArrowDisabled"),this.currSlideId=== 33829this.numSlides-1?this._d2.addClass("rsArrowDisabled"):this._d2.removeClass("rsArrowDisabled"))))},_x3:function(b,f,c,a,e){function g(){var a;h&&(a=h.data("rsTimeout"))&&(h!==k&&h.css({opacity:0,display:"none",zIndex:0}),clearTimeout(a),h.data("rsTimeout",""));if(a=k.data("rsTimeout"))clearTimeout(a),k.data("rsTimeout","")}var d=this,h,k,l={};isNaN(d._c)&&(d._c=400);d._p=d._h3=b;d.ev.trigger("rsBeforeAnimStart");d._e?d._l?(d._c=parseInt(d._c,10),c=d._g+d._v1,l[d._g+d._u1]=d._c+"ms",l[c]=a?n.rsCSS3Easing[d.st.easeInOut]: 33830n.rsCSS3Easing[d.st.easeOut],d._p1.css(l),a||!d.hasTouch?setTimeout(function(){d._p3(b)},5):d._p3(b)):(d._c=d.st.transitionSpeed,h=d._g4,k=d._r1,k.data("rsTimeout")&&k.css("opacity",0),g(),h&&h.data("rsTimeout",setTimeout(function(){l[d._g+d._u1]="0ms";l.zIndex=0;l.display="none";h.data("rsTimeout","");h.css(l);setTimeout(function(){h.css("opacity",0)},16)},d._c+60)),l.display="block",l.zIndex=d._m,l.opacity=0,l[d._g+d._u1]="0ms",l[d._g+d._v1]=n.rsCSS3Easing[d.st.easeInOut],k.css(l),k.data("rsTimeout", 33831setTimeout(function(){k.css(d._g+d._u1,d._c+"ms");k.data("rsTimeout",setTimeout(function(){k.css("opacity",1);k.data("rsTimeout","")},20))},20))):d._l?(l[d._h?d._x1:d._w1]=b+"px",d._p1.animate(l,d._c,a?d.st.easeInOut:d.st.easeOut)):(h=d._g4,k=d._r1,k.stop(!0,!0).css({opacity:0,display:"block",zIndex:d._m}),d._c=d.st.transitionSpeed,k.animate({opacity:1},d._c,d.st.easeInOut),g(),h&&h.data("rsTimeout",setTimeout(function(){h.stop(!0,!0).css({opacity:0,display:"none",zIndex:0})},d._c+60)));d._r2=!0; 33832d.loadingTimeout&&clearTimeout(d.loadingTimeout);d.loadingTimeout=e?setTimeout(function(){d.loadingTimeout=null;e.call()},d._c+60):setTimeout(function(){d.loadingTimeout=null;d._k4(f)},d._c+60)},_u2:function(b){this._r2=!1;clearTimeout(this.loadingTimeout);if(this._l)if(!this._e)this._p1.stop(!0),this._p=parseInt(this._p1.css(this._x1),10);else{if(!b){b=this._p;var f=this._h3=this._l4();this._p1.css(this._g+this._u1,"0ms");b!==f&&this._p3(f)}}else 20<this._m?this._m=10:this._m++},_l4:function(){var b= 33833window.getComputedStyle(this._p1.get(0),null).getPropertyValue(this._g+"transform").replace(/^matrix\(/i,"").split(/, |\)$/g),f=0===b[0].indexOf("matrix3d");return parseInt(b[this._h?f?12:4:f?13:5],10)},_m4:function(b,f){return this._e?this._y1+(f?b+this._z1+0:0+this._z1+b)+this._a2:b},_k4:function(b){this._l||(this._r1.css("z-index",0),this._m=10);this._r2=!1;this.staticSlideId=this.currSlideId;this._n2();this._n4=!1;this.ev.trigger("rsAfterSlideChange")},_i4:function(b,f){var c=this,a=(-c._u-c._d1)* 33834c._w;if(0!==c.numSlides&&!c._r2)if(c.st.loopRewind)c.goTo("left"===b?c.numSlides-1:0,f);else if(c._l){c._c=200;var e=function(){c._r2=!1};c._x3(a+("left"===b?30:-30),"",!1,!0,function(){c._r2=!1;c._x3(a,"",!1,!0,e)})}}
33834,_q2:function(b,f){if(!b.isRendered){var c=b.content,a="rsMainSlideImage",e,g=this.st.imageAlignCenter,d=this.st.imageScaleMode,h;b.videoURL&&(a="rsVideoContainer","fill"!==d?e=!0:(h=c,h.hasClass(a)||(h=h.find("."+a)),h.css({width:"100%",height:"100%"}),a="rsMainSlideImage"));c.hasClass(a)|| 33835(c=c.find("."+a));if(c){var k=b.iW,l=b.iH;b.isRendered=!0;if("none"!==d||g){a="fill"!==d?this._d4:0;h=this._b4-2*a;var n=this._c4-2*a,m,p,q={};"fit-if-smaller"===d&&(k>h||l>n)&&(d="fit");if("fill"===d||"fit"===d)m=h/k,p=n/l,m="fill"==d?m>p?m:p:"fit"==d?m<p?m:p:1,k=Math.ceil(k*m,10),l=Math.ceil(l*m,10);"none"!==d&&(q.width=k,q.height=l,e&&c.find(".rsImg").css({width:"100%",height:"100%"}));g&&(q.marginLeft=Math.floor((h-k)/2)+a,q.marginTop=Math.floor((n-l)/2)+a);c.css(q)}}}}};n.rsProto=u.prototype; 33836n.fn.royalSlider=function(b){var f=arguments;return this.each(function(){var c=n(this);if("object"!==typeof b&&b){if((c=c.data("royalSlider"))&&c[b])return c[b].apply(c,Array.prototype.slice.call(f,1))}else c.data("royalSlider")||c.data("royalSlider",new u(c,b))})};n.fn.royalSlider.defaults={slidesSpacing:8,startSlideId:0,loop:!1,loopRewind:!1,numImagesToPreload:4,fadeinLoadedSlide:!0,slidesOrientation:"horizontal",transitionType:"move",transitionSpeed:600,controlNavigation:"bullets",controlsInside:!0, 33837arrowsNav:!0,arrowsNavAutoHide:!0,navigateByClick:!0,randomizeSlides:!1,sliderDrag:!0,sliderTouch:!0,keyboardNavEnabled:!1,fadeInAfterLoaded:!0,allowCSS3:!0,allowCSS3OnWebkit:!0,addActiveClass:!1,autoHeight:!1,easeOut:"easeOutSine",easeInOut:"easeInOutSine",minSlideOffset:10,imageScaleMode:"fit-if-smaller",imageAlignCenter:!0,imageScalePadding:4,usePreloader:!0,autoScaleSlider:!1,autoScaleSliderWidth:800,autoScaleSliderHeight:400,autoScaleHeight:!0,arrowsNavHideOnTouch:!1,globalCaption:!1,slidesDiff:2}; 33838n.rsCSS3Easing={easeOutSine:"cubic-bezier(0.390, 0.575, 0.565, 1.000)",easeInOutSine:"cubic-bezier(0.445, 0.050, 0.550, 0.950)"};n.extend(jQuery.easing,{easeInOutSine:function(b,f,c,a,e){return-a/2*(Math.cos(Math.PI*f/e)-1)+c},easeOutSine:function(b,f,c,a,e){return a*Math.sin(f/e*(Math.PI/2))+c},easeOutCubic:function(b,f,c,a,e){return a*((f=f/e-1)*f*f+1)+c}})})(jQuery,window); 33839// jquery.rs.active-class v1.0.1 33840(function(c){c.rsProto._o4=function(){var b,a=this;if(a.st.addActiveClass)a.ev.on("rsOnUpdateNav",function(){b&&clearTimeout(b);b=setTimeout(function(){a._g4&&a._g4.removeClass("rsActiveSlide");a._r1&&a._r1.addClass("rsActiveSlide");b=null},50)})};c.rsModules.activeClass=c.rsProto._o4})(jQuery); 33841// jquery.rs.animated-blocks v1.0.7 33842(function(l){l.extend(l.rsProto,{_p4:function(){function m(){var g=a.currSlide;if(a.currSlide&&a.currSlide.isLoaded&&a._t4!==g){if(0<a._s4.length){for(b=0;b<a._s4.length;b++)clearTimeout(a._s4[b]);a._s4=[]}if(0<a._r4.length){var f;for(b=0;b<a._r4.length;b++)if(f=a._r4[b])a._e?(f.block.css(a._g+a._u1,"0s"),f.block.css(f.css)):f.block.stop(!0).css(f.css),a._t4=null,g.animBlocksDisplayed=!1;a._r4=[]}g.animBlocks&&(g.animBlocksDisplayed=!0,a._t4=g,a._u4(g.animBlocks))}}var a=this,b;a._q4={fadeEffect:!0, 33843moveEffect:"top",moveOffset:20,speed:400,easing:"easeOutSine",delay:200};a.st.block=l.extend({},a._q4,a.st.block);a._r4=[];a._s4=[];a.ev.on("rsAfterInit",function(){m()});a.ev.on("rsBeforeParseNode",function(a,b,d){b=l(b);d.animBlocks=b.find(".rsABlock").css("display","none");d.animBlocks.length||(b.hasClass("rsABlock")?d.animBlocks=b.css("display","none"):d.animBlocks=!1)});a.ev.on("rsAfterContentSet",function(b,f){f.id===a.slides[a.currSlideId].id&&setTimeout(function(){m()},a.st.fadeinLoadedSlide? 33844300:0)});a.ev.on("rsAfterSlideChange",function(){m()})},_v4:function(l,a){setTimeout(function(){l.css(a)},6)},_u4:function(m){var a=this,b,g,f,d,h,e,n;a._s4=[];m.each(function(m){b=l(this);g={};f={};d=null;var c=b.attr("data-move-offset"),c=c?parseInt(c,10):a.st.block.moveOffset;if(0<c&&((e=b.data("move-effect"))?(e=e.toLowerCase(),"none"===e?e=!1:"left"!==e&&"top"!==e&&"bottom"!==e&&"right"!==e&&(e=a.st.block.moveEffect,"none"===e&&(e=!1))):e=a.st.block.moveEffect,e&&"none"!==e)){var p;p="right"=== 33845e||"left"===e?!0:!1;var k;n=!1;a._e?(k=0,h=a._x1):(p?isNaN(parseInt(b.css("right"),10))?h="left":(h="right",n=!0):isNaN(parseInt(b.css("bottom"),10))?h="top":(h="bottom",n=!0),h="margin-"+h,n&&(c=-c),a._e?k=parseInt(b.css(h),10):(k=b.data("rs-start-move-prop"),void 0===k&&(k=parseInt(b.css(h),10),isNaN(k)&&(k=0),b.data("rs-start-move-prop",k))));f[h]=a._m4("top"===e||"left"===e?k-c:k+c,p);g[h]=a._m4(k,p)}c=b.attr("data-fade-effect");if(!c)c=a.st.block.fadeEffect;else if("none"===c.toLowerCase()|| 33846"false"===c.toLowerCase())c=!1;c&&(f.opacity=0,g.opacity=1);if(c||e)d={}
33846,d.hasFade=Boolean(c),Boolean(e)&&(d.moveProp=h,d.hasMove=!0),d.speed=b.data("speed"),isNaN(d.speed)&&(d.speed=a.st.block.speed),d.easing=b.data("easing"),d.easing||(d.easing=a.st.block.easing),d.css3Easing=l.rsCSS3Easing[d.easing],d.delay=b.data("delay"),isNaN(d.delay)&&(d.delay=a.st.block.delay*m);c={};a._e&&(c[a._g+a._u1]="0ms");c.moveProp=g.moveProp;c.opacity=g.opacity;c.display="none";a._r4.push({block:b,css:c});a._v4(b, 33847f);a._s4.push(setTimeout(function(b,d,c,e){return function(){b.css("display","block");if(c){var g={};if(a._e){var f="";c.hasMove&&(f+=c.moveProp);c.hasFade&&(c.hasMove&&(f+=", "),f+="opacity");g[a._g+a._t1]=f;g[a._g+a._u1]=c.speed+"ms";g[a._g+a._v1]=c.css3Easing;b.css(g);setTimeout(function(){b.css(d)},24)}else setTimeout(function(){b.animate(d,c.speed,c.easing)},16)}delete a._s4[e]}}(b,g,d,m),6>=d.delay?12:d.delay))})}});l.rsModules.animatedBlocks=l.rsProto._p4})(jQuery); 33848// jquery.rs.auto-height v1.0.3 33849(function(b){b.extend(b.rsProto,{_w4:function(){var a=this;if(a.st.autoHeight){var b,c,e,f=!0,d=function(d){e=a.slides[a.currSlideId];(b=e.holder)&&(c=b.height())&&void 0!==c&&c>(a.st.minAutoHeight||30)&&(a._c4=c,a._e||!d?a._e1.css("height",c):a._e1.stop(!0,!0).animate({height:c},a.st.transitionSpeed),a.ev.trigger("rsAutoHeightChange",c),f&&(a._e&&setTimeout(function(){a._e1.css(a._g+"transition","height "+a.st.transitionSpeed+"ms ease-in-out")},16),f=!1))};a.ev.on("rsMaybeSizeReady.rsAutoHeight", 33850function(a,b){e===b&&d()});a.ev.on("rsAfterContentSet.rsAutoHeight",function(a,b){e===b&&d()});a.slider.addClass("rsAutoHeight");a.ev.one("rsAfterInit",function(){setTimeout(function(){d(!1);setTimeout(function(){a.slider.append('<div style="clear:both; float: none;"></div>')},16)},16)});a.ev.on("rsBeforeAnimStart",function(){d(!0)});a.ev.on("rsBeforeSizeSet",function(){setTimeout(function(){d(!1)},16)})}}});b.rsModules.autoHeight=b.rsProto._w4})(jQuery); 33851// jquery.rs.autoplay v1.0.5 33852(function(b){b.extend(b.rsProto,{_x4:function(){var a=this,d;
33852a._y4={enabled:!1,stopAtAction:!0,pauseOnHover:!0,delay:2E3};!a.st.autoPlay&&a.st.autoplay&&(a.st.autoPlay=a.st.autoplay);a.st.autoPlay=b.extend({},a._y4,a.st.autoPlay);a.st.autoPlay.enabled&&(a.ev.on("rsBeforeParseNode",function(a,c,f){c=b(c);if(d=c.attr("data-rsDelay"))f.customDelay=parseInt(d,10)}),a.ev.one("rsAfterInit",function(){a._z4()}),a.ev.on("rsBeforeDestroy",function(){a.stopAutoPlay();a.slider.off("mouseenter mouseleave");b(window).off("blur"+ 33853a.ns+" focus"+a.ns)}))},_z4:function(){var a=this;a.startAutoPlay();a.ev.on("rsAfterContentSet",function(b,e){a._l2||a._r2||!a._a5||e!==a.currSlide||a._b5()});a.ev.on("rsDragRelease",function(){a._a5&&a._c5&&(a._c5=!1,a._b5())});a.ev.on("rsAfterSlideChange",function(){a._a5&&a._c5&&(a._c5=!1,a.currSlide.isLoaded&&a._b5())});a.ev.on("rsDragStart",function(){a._a5&&(a.st.autoPlay.stopAtAction?a.stopAutoPlay():(a._c5=!0,a._d5()))});a.ev.on("rsBeforeMove",function(b,e,c){a._a5&&(c&&a.st.autoPlay.stopAtAction? 33854a.stopAutoPlay():(a._c5=!0,a._d5()))});a._e5=!1;a.ev.on("rsVideoStop",function(){a._a5&&(a._e5=!1,a._b5())});a.ev.on("rsVideoPlay",function(){a._a5&&(a._c5=!1,a._d5(),a._e5=!0)});b(window).on("blur"+a.ns,function(){a._a5&&(a._c5=!0,a._d5())}).on("focus"+a.ns,function(){a._a5&&a._c5&&(a._c5=!1,a._b5())});a.st.autoPlay.pauseOnHover&&(a._f5=!1,a.slider.hover(function(){a._a5&&(a._c5=!1,a._d5(),a._f5=!0)},function(){a._a5&&(a._f5=!1,a._b5())}))},toggleAutoPlay:function(){this._a5?this.stopAutoPlay(): 33855this.startAutoPlay()},startAutoPlay:function(){this._a5=!0;this.currSlide.isLoaded&&this._b5()},stopAutoPlay:function(){this._e5=this._f5=this._c5=this._a5=!1;this._d5()},_b5:function(){var a=this;a._f5||a._e5||(a._g5=!0,a._h5&&clearTimeout(a._h5),a._h5=setTimeout(function(){var b;a._z||a.st.loopRewind||(b=!0,a.st.loopRewind=!0);a.next(!0);b&&(a.st.loopRewind=!1)},a.currSlide.customDelay?a.currSlide.customDelay:a.st.autoPlay.delay))},_d5:function(){this._f5||this._e5||(this._g5=!1,this._h5&&(clearTimeout(this._h5), 33856this._h5=null))}});b.rsModules.autoplay=b.rsProto._x4})(jQuery); 33857// jquery.rs.bullets v1.0.1 33858(function(c){c.extend(c.rsProto,{_i5:function(){var a=this;"bullets"===a.st.controlNavigation&&(a.ev.one("rsAfterPropsSetup",function(){a._j5=!0;a.slider.addClass("rsWithBullets");for(var b='<div class="rsNav rsBullets">',e=0;e<a.numSlides;e++)b+='<div class="rsNavItem rsBullet"><span></span></div>';a._k5=b=c(b+"</div>");a._l5=b.appendTo(a.slider).children();a._k5.on("click.rs",".rsNavItem",function(b){a._m5||a.goTo(c(this).index())})}),a.ev.on("rsOnAppendSlide",function(b,c,d){d>=a.numSlides?a._k5.append('<div class="rsNavItem rsBullet"><span></span></div>'): 33859a._l5.eq(d).before('<div class="rsNavItem rsBullet"><span></span></div>');a._l5=a._k5.children()}),a.ev.on("rsOnRemoveSlide",function(b,c){var d=a._l5.eq(c);d&&d.length&&(d.remove(),a._l5=a._k5.children())}),a.ev.on("rsOnUpdateNav",function(){var b=a.currSlideId;a._n5&&a._n5.removeClass("rsNavSelected");b=a._l5.eq(b);b.addClass("rsNavSelected");a._n5=b}))}});c.rsModules.bullets=c.rsProto._i5})(jQuery); 33860// jquery.rs.deeplinking v1.0.6 + jQuery hashchange plugin v1.3 Copyright (c) 2010 Ben Alman 33861(function(d){d.extend(d.rsProto,{_o5:function(){var a=this,l,g,f;a._p5={enabled:!1,change:!1,prefix:""};a.st.deeplinking=d.extend({},a._p5,a.st.deeplinking);if(a.st.deeplinking.enabled){var k=a.st.deeplinking.change,c=a.st.deeplinking.prefix,e="#"+c,h=function(){var b=window.location.hash;return b&&0<b.indexOf(c)&&(b=parseInt(b.substring(e.length),10),0<=b)?b-1:-1},m=h();-1!==m&&(a.st.startSlideId=m);k&&(d(window).on("hashchange"+a.ns,function(b){l||(b=h(),0>b||(b>a.numSlides-1&&(b=a.numSlides-1), 33862a.goTo(b)))}),a.ev.on("rsBeforeAnimStart",function(){g&&clearTimeout(g);f&&clearTimeout(f)}),a.ev.on("rsAfterSlideChange",function(){g&&clearTimeout(g);f&&clearTimeout(f);f=setTimeout(function(){l=!0;window.location.replace((""+window.location).split("#")[0]+e+(a.currSlideId+1));g=setTimeout(function(){l=!1;g=null},60)},400)}));a.ev.on("rsBeforeDestroy",function(){g=f=null;k&&d(window).off("hashchange"+a.ns)})}}});d.rsModules.deeplinking=d.rsProto._o5})(jQuery); 33863(function(d,a,l){function g(b){b=b||location.href;return"#"+b.replace(/^[^#]*#?(.*)$/,"$1")}"$:nomunge";var f="hashchange",k=document,c,e=d.event.special,h=k.documentMode,m="on"+f in a&&(h===l||7<h);d.fn[f]=function(b){return b?this.bind(f,b):this.trigger(f)};d.fn[f].delay=50;e[f]=d.extend(e[f],{setup:function(){if(m)return!1;d(c.start)},teardown:function(){if(m)return!1;
33863d(c.stop)}});c=function(){function b(){var c=g(),n=r(h);c!==h?(p(h=c,n),d(a).trigger(f)):n!==h&&(location.href=location.href.replace(/#.*/, 33864"")+n);e=setTimeout(b,d.fn[f].delay)}var c={},e,h=g(),q=function(b){return b},p=q,r=q;c.start=function(){e||b()};c.stop=function(){e&&clearTimeout(e);e=l};a.attachEvent&&!a.addEventListener&&!m&&function(){var a,e;c.start=function(){a||(e=(e=d.fn[f].src)&&e+g(),a=d('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){e||p(g());b()}).attr("src",e||"javascript:0").insertAfter("body")[0].contentWindow,k.onpropertychange=function(){try{"title"===event.propertyName&&(a.document.title= 33865k.title)}catch(b){}})};c.stop=q;r=function(){return g(a.location.href)};p=function(b,e){var c=a.document,g=d.fn[f].domain;b!==e&&(c.title=k.title,c.open(),g&&c.write('<script>document.domain="'+g+'"\x3c/script>'),c.close(),a.location.hash=b)}}();return c}()})(jQuery,this); 33866// jquery.rs.fullscreen v1.0.6 33867(function(c){c.extend(c.rsProto,{_q5:function(){var a=this;a._r5={enabled:!1,keyboardNav:!0,buttonFS:!0,nativeFS:!1,doubleTap:!0};a.st.fullscreen=c.extend({},a._r5,a.st.fullscreen);if(a.st.fullscreen.enabled)a.ev.one("rsBeforeSizeSet",function(){a._s5()})},_s5:function(){var a=this;a._t5=!a.st.keyboardNavEnabled&&a.st.fullscreen.keyboardNav;
33867if(a.st.fullscreen.nativeFS){var b={supportsFullScreen:!1,isFullScreen:function(){return!1},requestFullScreen:function(){},cancelFullScreen:function(){},fullScreenEventName:"", 33868prefix:""},d=["webkit","moz","o","ms","khtml"];if("undefined"!=typeof document.cancelFullScreen)b.supportsFullScreen=!0;else for(var e=0,f=d.length;e<f;e++)if(b.prefix=d[e],"undefined"!=typeof document[b.prefix+"CancelFullScreen"]){b.supportsFullScreen=!0;break}b.supportsFullScreen?(a.nativeFS=!0,b.fullScreenEventName=b.prefix+"fullscreenchange"+a.ns,b.isFullScreen=function(){switch(this.prefix){case "":return document.fullScreen;case "webkit":return document.webkitIsFullScreen;default:return document[this.prefix+ 33869"FullScreen"]}},b.requestFullScreen=function(a){return""===this.prefix?a.requestFullScreen():a[this.prefix+"RequestFullScreen"]()},b.cancelFullScreen=function(a){return""===this.prefix?document.cancelFullScreen():document[this.prefix+"CancelFullScreen"]()},a._u5=b):a._u5=!1}a.st.fullscreen.buttonFS&&(a._v5=c('<div class="rsFullscreenBtn"><div class="rsFullscreenIcn"></div></div>').appendTo(a._o1).on("click.rs",function(){a.isFullscreen?a.exitFullscreen():a.enterFullscreen()}))},enterFullscreen:function(a){var b= 33870this;if(b._u5)if(a)b._u5.requestFullScreen(c("html")[0]);else{b._b.on(b._u5.fullScreenEventName,function(a){b._u5.isFullScreen()?b.enterFullscreen(!0):b.exitFullscreen(!0)});b._u5.requestFullScreen(c("html")[0]);return}if(!b._w5){b._w5=!0;b._b.on("keyup"+b.ns+"fullscreen",function(a){27===a.keyCode&&b.exitFullscreen()});b._t5&&b._b2();a=c(window);b._x5=a.scrollTop();b._y5=a.scrollLeft();b._z5=c("html").attr("style");b._a6=c("body").attr("style");b._b6=b.slider.attr("style");c("body, html").css({overflow:"hidden", 33871height:"100%",width:"100%",margin:"0",padding:"0"});b.slider.addClass("rsFullscreen");var d;for(d=0;d<b.numSlides;d++)a=b.slides[d],a.isRendered=!1,a.bigImage&&(a.isBig=!0,a.isMedLoaded=a.isLoaded,a.isMedLoading=a.isLoading,a.medImage=a.image,a.medIW=a.iW,a.medIH=a.iH,a.slideId=-99,a.bigImage!==a.medImage&&(a.sizeType="big"),a.isLoaded=a.isBigLoaded,a.isLoading=!1,a.image=a.bigImage,a.images[0]=a.bigImage,a.iW=a.bigIW,a.iH=a.bigIH,a.isAppended=a.contentAdded=!1,b._c6(a));b.isFullscreen=!0;b._w5=!1; 33872b.updateSliderSize();b.ev.trigger("rsEnterFullscreen")}},exitFullscreen:function(a){var b=this;if(b._u5){if(!a){b._u5.cancelFullScreen(c("html")[0]);return}b._b.off(b._u5.fullScreenEventName)}if(!b._w5){b._w5=!0;b._b.off("keyup"+b.ns+"fullscreen");b._t5&&b._b.off("keydown"+b.ns);c("html").attr("style",b._z5||"");c("body").attr("style",b._a6||"");var d;for(d=0;d<b.numSlides;d++)a=b.slides[d],a.isRendered=!1,a.bigImage&&(a.isBig=!1,a.slideId=-99,a.isBigLoaded=a.isLoaded,a.isBigLoading=a.isLoading,a.bigImage= 33873a.image,a.bigIW=a.iW,a.bigIH=a.iH,a.isLoaded=a.isMedLoaded,a.isLoading=!1,a.image=a.medImage,a.images[0]=a.medImage,a.iW=a.medIW,a.iH=a.medIH,a.isAppended=a.contentAdded=!1,b._c6(a,!0),a.bigImage!==a.medImage&&(a.sizeType="med"));b.isFullscreen=!1;a=c(window);a.scrollTop(b._x5);a.scrollLeft(b._y5);b._w5=!1;b.slider.removeClass("rsFullscreen");b.updateSliderSize();setTimeout(function(){b.updateSliderSize()},1);b.ev.trigger("rsExitFullscreen")}},_c6:function(a,b){var d=a.isLoaded||a.isLoading?'<img class="rsImg rsMainSlideImage" src="'+ 33874a.image+'"/>':'<a class="rsImg rsMainSlideImage" href="'+a.image+'"></a>';a.content.hasClass("rsImg")?a.content=c(d):a.content.find(".rsImg").eq(0).replaceWith(d);a.isLoaded||a.isLoading||!a.holder||a.holder.html(a.content)}});c.rsModules.fullscreen=c.rsProto._q5})(jQuery); 33875// jquery.rs.global-caption v1.0 33876(function(b){b.extend(b.rsProto,{_d6:function(){var a=this;a.st.globalCaption&&(a.ev.on("rsAfterInit",function(){a.globalCaption=b('<div class="rsGCaption"></div>').appendTo(a.st.globalCaptionInside?a._e1:a.slider);a.globalCaption.html(a.currSlide.caption)}),a.ev.on("rsBeforeAnimStart",function(){a.globalCaption.html(a.currSlide.caption)}))}});b.rsModules.globalCaption=b.rsProto._d6})(jQuery); 33877// jquery.rs.nav-auto-hide v1.0 33878(function(b){b.extend(b.rsProto,{_e6:function(){var a=this;if(a.st.navAutoHide&&!a.hasTouch)a.ev.one("rsAfterInit",function(){if(a._k5){a._k5.addClass("rsHidden");var b=a.slider;b.one("mousemove.controlnav",function(){a._k5.removeClass("rsHidden")});b.hover(function(){a._k5.removeClass("rsHidden")},function(){a._k5.addClass("rsHidden")})}})}});b.rsModules.autoHideNav=b.rsProto._e6})(jQuery);
33879// jquery.rs.tabs v1.0.2 33880(function(e){e.extend(e.rsProto,{_f6:function(){var a=this;"tabs"===a.st.controlNavigation&&(a.ev.on("rsBeforeParseNode",function(a,d,b){d=e(d);b.thumbnail=d.find(".rsTmb").remove();b.thumbnail.length?b.thumbnail=e(document.createElement("div")).append(b.thumbnail).html():(b.thumbnail=d.attr("data-rsTmb"),b.thumbnail||(b.thumbnail=d.find(".rsImg").attr("data-rsTmb")),b.thumbnail=b.thumbnail?'<img src="'+b.thumbnail+'"/>':"")}),a.ev.one("rsAfterPropsSetup",function(){a._g6()}),a.ev.on("rsOnAppendSlide", 33881function(c,d,b){b>=a.numSlides?a._k5.append('<div class="rsNavItem rsTab">'+d.thumbnail+"</div>"):a._l5.eq(b).before('<div class="rsNavItem rsTab">'+item.thumbnail+"</div>");a._l5=a._k5.children()}),a.ev.on("rsOnRemoveSlide",function(c,d){var b=a._l5.eq(d);b&&(b.remove(),a._l5=a._k5.children())}),a.ev.on("rsOnUpdateNav",function(){var c=a.currSlideId;a._n5&&a._n5.removeClass("rsNavSelected");c=a._l5.eq(c);c.addClass("rsNavSelected");a._n5=c}))},_g6:function(){var a=this,c;a._j5=!0;c='<div class="rsNav rsTabs">'; 33882for(var d=0;d<a.numSlides;d++)c+='<div class="rsNavItem rsTab">'+a.slides[d].thumbnail+"</div>";c=e(c+"</div>");a._k5=c;a._l5=c.children(".rsNavItem");a.slider.append(c);a._k5.click(function(b){b=e(b.target).closest(".rsNavItem");b.length&&a.goTo(b.index())})}});e.rsModules.tabs=e.rsProto._f6})(jQuery); 33883// jquery.rs.thumbnails v1.0.7 33884(function(f){f.extend(f.rsProto,{_h6:function(){var a=this;"thumbnails"===a.st.controlNavigation&&(a._i6={drag:!0,touch:!0,orientation:"horizontal",navigation:!0,arrows:!0,arrowLeft:null,arrowRight:null,spacing:4,arrowsAutoHide:!1,appendSpan:!1,transitionSpeed:600,autoCenter:!0,fitInViewport:!0,firstMargin:!0,paddingTop:0,paddingBottom:0},a.st.thumbs=f.extend({},a._i6,a.st.thumbs),a._j6=!0,!1===a.st.thumbs.firstMargin?a.st.thumbs.firstMargin=0:!0===a.st.thumbs.firstMargin&&(a.st.thumbs.firstMargin= 33885a.st.thumbs.spacing),a.ev.on("rsBeforeParseNode",function(a,b,c){b=f(b);c.thumbnail=b.find(".rsTmb").remove();c.thumbnail.length?c.thumbnail=f(document.createElement("div")).append(c.thumbnail).html():(c.thumbnail=b.attr("data-rsTmb"),c.thumbnail||(c.thumbnail=b.find(".rsImg").attr("data-rsTmb")),c.thumbnail=c.thumbnail?'<img src="'+c.thumbnail+'"/>':"")}),a.ev.one("rsAfterPropsSetup",function(){a._k6()}),a._n5=null,a.ev.on("rsOnUpdateNav",function(){var e=f(a._l5[a.currSlideId]);e!==a._n5&&(a._n5&& 33886(a._n5.removeClass("rsNavSelected"),a._n5=null),a._l6&&a._m6(a.currSlideId),a._n5=e.addClass("rsNavSelected"))}),a.ev.on("rsOnAppendSlide",function(e,b,c){e="<div"+a._n6+' class="rsNavItem rsThumb">'+a._o6+b.thumbnail+"</div>";a._e&&a._s3.css(a._g+"transition-duration","0ms");c>=a.numSlides?a._s3.append(e):a._l5.eq(c).before(e);a._l5=a._s3.children();a.updateThumbsSize(!0)}),a.ev.on("rsOnRemoveSlide",function(e,b){var c=a._l5.eq(b);c&&(a._e&&a._s3.css(a._g+"transition-duration","0ms"),c.remove(), 33887a._l5=a._s3.children(),a.updateThumbsSize(!0))}))},_k6:function(){var a=this,e="rsThumbs",b=a.st.thumbs,c="",g,d,h=b.spacing;a._j5=!0;a._e3="vertical"===b.orientation?!1:!0;a._n6=g=h?' style="margin-'+(a._e3?"right":"bottom")+":"+h+'px;"':"";a._i3=0;a._p6=!1;a._m5=!1;a._l6=!1;a._q6=b.arrows&&b.navigation;d=a._e3?"Hor":"Ver";a.slider.addClass("rsWithThumbs rsWithThumbs"+d);c+='<div class="rsNav rsThumbs rsThumbs'+d+'"><div class="'+e+'Container">';a._o6=b.appendSpan?'<span class="thumbIco"></span>': 33888"";for(var k=0;k<a.numSlides;k++)d=a.slides[k],c+="<div"+g+' class="rsNavItem rsThumb">'+d.thumbnail+a._o6+"</div>";c=f(c+"</div></div>");g={};b.paddingTop&&(g[a._e3?"paddingTop":"paddingLeft"]=b.paddingTop);b.paddingBottom&&(g[a._e3?"paddingBottom":"paddingRight"]=b.paddingBottom);c.css(g);a._s3=f(c).find("."+e+"Container");a._q6&&(e+="Arrow",b.arrowLeft?a._r6=b.arrowLeft:(a._r6=f('<div class="'+e+" "+e+'Left"><div class="'+e+'Icn"></div></div>'),c.append(a._r6)),b.arrowRight?a._s6=b.arrowRight: 33889(a._s6=f('<div class="'+e+" "+e+'Right"><div class="'+e+'Icn"></div></div>'),c.append(a._s6)),a._r6.click(function(){var b=(Math.floor(a._i3/a._t6)+a._u6)*a._t6+a.st.thumbs.firstMargin;a._a4(b>a._n3?a._n3:b)}),a._s6.click(function(){var b=(Math.floor(a._i3/a._t6)-a._u6)*a._t6+a.st.thumbs.firstMargin;a._a4(b<a._o3?a._o3:b)}),b.arrowsAutoHide&&!a.hasTouch&&(a._r6.css("opacity",0),a._s6.css("opacity",0),c.one("mousemove.rsarrowshover",function(){a._l6&&(a._r6.css("opacity",1),a._s6.css("opacity",1))}), 33890c.hover(function(){a._l6&&(a._r6.css("opacity",1),a._s6.css("opacity",1))},function(){a._l6&&(a._r6.css("opacity",0),a._s6.css("opacity",0)
33890)})));a._k5=c;a._l5=a._s3.children();a.msEnabled&&a.st.thumbs.navigation&&a._s3.css("-ms-touch-action",a._e3?"pan-y":"pan-x");a.slider.append(c);a._w3=!0;a._v6=h;b.navigation&&a._e&&a._s3.css(a._g+"transition-property",a._g+"transform");a._k5.on("click.rs",".rsNavItem",function(b){a._m5||a.goTo(f(this).index())});a.ev.off("rsBeforeSizeSet.thumbs").on("rsBeforeSizeSet.thumbs", 33891function(){a._w6=a._e3?a._c4:a._b4;a.updateThumbsSize(!0)});a.ev.off("rsAutoHeightChange.thumbs").on("rsAutoHeightChange.thumbs",function(b,c){a.updateThumbsSize(!0,c)})},updateThumbsSize:function(a,e){var b=this,c=b._l5.first(),f={},d=b._l5.length;b._t6=(b._e3?c.outerWidth():c.outerHeight())+b._v6;b._y3=d*b._t6-b._v6;f[b._e3?"width":"height"]=b._y3+b._v6;b._z3=b._e3?b._k5.width():void 0!==e?e:b._k5.height();b._w3&&(b.isFullscreen||b.st.thumbs.fitInViewport)&&(b._e3?b._c4=b._w6-b._k5.outerHeight(): 33892b._b4=b._w6-b._k5.outerWidth());b._z3&&(b._o3=-(b._y3-b._z3)-b.st.thumbs.firstMargin,b._n3=b.st.thumbs.firstMargin,b._u6=Math.floor(b._z3/b._t6),b._y3<b._z3?(b.st.thumbs.autoCenter&&b._q3((b._z3-b._y3)/2),b.st.thumbs.arrows&&b._r6&&(b._r6.addClass("rsThumbsArrowDisabled"),b._s6.addClass("rsThumbsArrowDisabled")),b._l6=!1,b._m5=!1,b._k5.off(b._j1)):b.st.thumbs.navigation&&!b._l6&&(b._l6=!0,!b.hasTouch&&b.st.thumbs.drag||b.hasTouch&&b.st.thumbs.touch)&&(b._m5=!0,b._k5.on(b._j1,function(a){b._g2(a,!0)})), 33893b._s3.css(f),a&&e&&b._m6(b.currSlideId,!0))},setThumbsOrientation:function(a,e){this._w3&&(this.st.thumbs.orientation=a,this._k5.remove(),this.slider.removeClass("rsWithThumbsHor rsWithThumbsVer"),this._k6(),this._k5.off(this._j1),e||this.updateSliderSize(!0))},_q3:function(a){this._i3=a;this._e?this._s3.css(this._x1,this._y1+(this._e3?a+this._z1+0:0+this._z1+a)+this._a2):this._s3.css(this._e3?this._x1:this._w1,a)},_a4:function(a,e,b,c,g){var d=this;if(d._l6){e||(e=d.st.thumbs.transitionSpeed);d._i3= 33894a;d._x6&&clearTimeout(d._x6);d._p6&&(d._e||d._s3.stop(),b=!0);var h={};d._p6=!0;d._e?(h[d._g+"transition-duration"]=e+"ms",h[d._g+"transition-timing-function"]=b?f.rsCSS3Easing[d.st.easeOut]:f.rsCSS3Easing[d.st.easeInOut],d._s3.css(h),d._q3(a)):(h[d._e3?d._x1:d._w1]=a+"px",d._s3.animate(h,e,b?"easeOutCubic":d.st.easeInOut));c&&(d._i3=c);d._y6();d._x6=setTimeout(function(){d._p6=!1;g&&(d._a4(c,g,!0),g=null)},e)}},_y6:function(){this._q6&&(this._i3===this._n3?this._r6.addClass("rsThumbsArrowDisabled"): 33895this._r6.removeClass("rsThumbsArrowDisabled"),this._i3===this._o3?this._s6.addClass("rsThumbsArrowDisabled"):this._s6.removeClass("rsThumbsArrowDisabled"))},_m6:function(a,e){var b=0,c,f=a*this._t6+2*this._t6-this._v6+this._n3,d=Math.floor(this._i3/this._t6);this._l6&&(this._j6&&(e=!0,this._j6=!1),f+this._i3>this._z3?(a===this.numSlides-1&&(b=1),d=-a+this._u6-2+b,c=d*this._t6+this._z3%this._t6+this._v6-this._n3):0!==a?(a-1)*this._t6<=-this._i3+this._n3&&a-1<=this.numSlides-this._u6&&(c=(-a+1)*this._t6+ 33896this._n3):c=this._n3,c!==this._i3&&(b=void 0===c?this._i3:c,b>this._n3?this._q3(this._n3):b<this._o3?this._q3(this._o3):void 0!==c&&(e?this._q3(c):this._a4(c))),this._y6())}});f.rsModules.thumbnails=f.rsProto._h6})(jQuery); 33897// jquery.rs.video v1.1.3 33898(function(f){f.extend(f.rsProto,{_z6:function(){var a=this;a._a7={autoHideArrows:!0,autoHideControlNav:!1,autoHideBlocks:!1,autoHideCaption:!1,disableCSS3inFF:!0,youTubeCode:'<iframe src="https://www.youtube.com/embed/%id%?rel=1&showinfo=0&autoplay=1&wmode=transparent" frameborder="no"></iframe>',vimeoCode:'<iframe src="http://player.vimeo.com/video/%id%?byline=0&portrait=0&autoplay=1" frameborder="no" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'};a.st.video=f.extend({},a._a7, 33899a.st.video);a.ev.on("rsBeforeSizeSet",function(){a._b7&&setTimeout(function(){var b=a._r1,b=b.hasClass("rsVideoContainer")?b:b.find(".rsVideoContainer");
33899a._c7&&a._c7.css({width:b.width(),height:b.height()})},32)});var d=a._a.mozilla;a.ev.on("rsAfterParseNode",function(b,c,e){b=f(c);if(e.videoURL){a.st.video.disableCSS3inFF&&d&&(a._e=a._f=!1);c=f('<div class="rsVideoContainer"></div>');var g=f('<div class="rsBtnCenterer"><div class="rsPlayBtn"><div class="rsPlayBtnIcon"></div></div></div>');b.hasClass("rsImg")? 33900e.content=c.append(b).append(g):e.content.find(".rsImg").wrap(c).after(g)}});a.ev.on("rsAfterSlideChange",function(){a.stopVideo()})},toggleVideo:function(){return this._b7?this.stopVideo():this.playVideo()},playVideo:function(){var a=this;if(!a._b7){var d=a.currSlide;if(!d.videoURL)return!1;a._d7=d;var b=a._e7=d.content,d=d.videoURL,c,e;d.match(/youtu\.be/i)||d.match(/youtube\.com/i)?(e=/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/,(e=d.match(e))&&11==e[7].length&& 33901(c=e[7]),void 0!==c&&(a._c7=a.st.video.youTubeCode.replace("%id%",c))):d.match(/vimeo\.com/i)&&(e=/(www\.)?vimeo.com\/(\d+)($|\/)/,(e=d.match(e))&&(c=e[2]),void 0!==c&&(a._c7=a.st.video.vimeoCode.replace("%id%",c)));a.videoObj=f(a._c7);a.ev.trigger("rsOnCreateVideoElement",[d]);a.videoObj.length&&(a._c7=f('<div class="rsVideoFrameHolder"><div class="rsPreloader"></div><div class="rsCloseVideoBtn"><div class="rsCloseVideoIcn"></div></div></div>'),a._c7.find(".rsPreloader").after(a.videoObj),b=b.hasClass("rsVideoContainer")?
33902b:b.find(".rsVideoContainer"),a._c7.css({width:b.width(),height:b.height()}).find(".rsCloseVideoBtn").off("click.rsv").on("click.rsv",function(b){a.stopVideo();b.preventDefault();b.stopPropagation();return!1}),b.append(a._c7),a.isIPAD&&b.addClass("rsIOSVideo"),a._f7(!1),setTimeout(function(){a._c7.addClass("rsVideoActive")},10),a.ev.trigger("rsVideoPlay"),a._b7=!0);return!0}return!1},stopVideo:function(){var a=this;return a._b7?(a.isIPAD&&a.slider.find(".rsCloseVideoBtn").remove(),a._f7(!0),setTimeout(function(){a.ev.trigger("rsOnDestroyVideoElement", 33903[a.videoObj]);var d=a._c7.find("iframe");if(d.length)try{d.attr("src","")}catch(b){}a._c7.remove();a._c7=null},16),a.ev.trigger("rsVideoStop"),a._b7=!1,!0):!1},_f7:function(a,d){var b=[],c=this.st.video;c.autoHideArrows&&(this._c2&&(b.push(this._c2,this._d2),this._e2=!a),this._v5&&b.push(this._v5));c.autoHideControlNav&&this._k5&&b.push(this._k5);c.autoHideBlocks&&this._d7.animBlocks&&b.push(this._d7.animBlocks);c.autoHideCaption&&this.globalCaption&&b.push(this.globalCaption);this.slider[a?"removeClass": 33904"addClass"]("rsVideoPlaying");if(b.length)for(c=0;c<b.length;c++)a?b[c].removeClass("rsHidden"):b[c].addClass("rsHidden")}});f.rsModules.video=f.rsProto._z6})(jQuery); 33905// jquery.rs.visible-nearby v1.0.2 33906(function(d){d.rsProto._g7=function(){var a=this;a.st.visibleNearby&&a.st.visibleNearby.enabled&&(a._h7={enabled:!0,centerArea:0.6,center:!0,breakpoint:0,breakpointCenterArea:0.8,hiddenOverflow:!0,navigateByCenterClick:!1},a.st.visibleNearby=d.extend({},a._h7,a.st.visibleNearby),a.ev.one("rsAfterPropsSetup",function(){a._i7=a._e1.css("overflow","visible").wrap('<div class="rsVisibleNearbyWrap"></div>').parent();a.st.visibleNearby.hiddenOverflow||a._i7.css("overflow","visible");a._o1=a.st.controlsInside? 33907a._i7:a.slider}),a.ev.on("rsAfterSizePropSet",function(){var b,c=a.st.visibleNearby;b=c.breakpoint&&a.width<c.breakpoint?c.breakpointCenterArea:c.centerArea;a._h?(a._b4*=b,a._i7.css({height:a._c4,width:a._b4/b}),a._d=a._b4*(1-b)/2/b):(a._c4*=b,a._i7.css({height:a._c4/b,width:a._b4}),a._d=a._c4*(1-b)/2/b);c.navigateByCenterClick||(a._q=a._h?a._b4:a._c4);c.center&&a._e1.css("margin-"+(a._h?"left":"top"),a._d)}))};d.rsModules.visibleNearby=d.rsProto._g7})(jQuery); 33908 33909/*! ScrollMagic v2.0.5 | (c) 2015 Jan Paepke (@janpaepke) | license & info: http://scrollmagic.io */ 33910 33911!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.ScrollMagic=t()}(this,function(){"use strict";var e=function(){};e.version="2.0.5",window.addEventListener("mousewheel",function(){});var t="data-scrollmagic-pin-spacer";e.Controller=function(r){var o,s,a="ScrollMagic.Controller",l="FORWARD",c="REVERSE",u="PAUSED",f=n.defaults,d=this,h=i.extend({},f,r),g=[],p=!1,v=0,m=u,w=!0,y=0,S=!0,b=function(){for(var e in h)f.hasOwnProperty(e)||delete h[e];if(h.container=i.get.elements(h.container)[0],!h.container)throw a+" init failed.";w=h.container===window||h.container===document.body||!document.body.contains(h.container),w&&(h.container=window),y=z(),h.container.addEventListener("resize",T),h.container.addEventListener("scroll",T),h.refreshInterval=parseInt(h.refreshInterval)||f.refreshInterval,E()},E=function(){h.refreshInterval>0&&(s=window.setTimeout(A,h.refreshInterval))},x=function(){return h.vertical?i.get.scrollTop(h.container):i.get.scrollLeft(h.container)},z=function(){return h.vertical?i.get.height(h.container):i.get.width(h.container)},C=this._setScrollPos=function(e){h.vertical?w?window.scrollTo(i.get.scrollLeft(),e):h.container.scrollTop=e:w?window.scrollTo(e,i.get.scrollTop()):h.container.scrollLeft=e},F=function(){if(S&&p){var e=i.type.Array(p)?p:g.slice(0);p=!1;var t=v;v=d.scrollPos();var n=v-t;0!==n&&(m=n>0?l:c),m===c&&e.reverse(),e.forEach(function(e){e.update(!0)})}},L=function(){o=i.rAF(F)},T=function(e){"resize"==e.type&&(y=z(),m=u),p!==!0&&(p=!0,L())},A=function(){if(!w&&y!=z()){var e;try{e=new Event("resize",{bubbles:!1,cancelable:!1})}catch(t){e=document.createEvent("Event"),e.initEvent("resize",!1,!1)}h.container.dispatchEvent(e)}g.forEach(function(e){e.refresh()}),E()};this._options=h;var O=function(e){if(e.length<=1)return e;var t=e.slice(0);return t.sort(function(e,t){return e.scrollOffset()>t.scrollOffset()?1:-1}),t};return this.addScene=function(t){if(i.type.Array(t))t.forEach(function(e){d.addScene(e)});else if(t instanceof e.Scene)if(t.controller()!==d)t.addTo(d);else if(g.indexOf(t)<0){g.push(t),g=O(g),t.on("shift.controller_sort",function(){g=O(g)});for(var n in h.globalSceneOptions)t[n]&&t[n].call(t,h.globalSceneOptions[n])}return d},this.removeScene=function(e){if(i.type.Array(e))e.forEach(function(e){d.removeScene(e)});else{var t=g.indexOf(e);t>-1&&(e.off("shift.controller_sort"),g.splice(t,1),e.remove())}return d},this.updateScene=function(t,n){return i.type.Array(t)?t.forEach(function(e){d.updateScene(e,n)}):n?t.update(!0):p!==!0&&t instanceof e.Scene&&(p=p||[],-1==p.indexOf(t)&&p.push(t),p=O(p),L()),d},this.update=function(e){return T({type:"resize"}),e&&F(),d},this.scrollTo=function(n,r){if(i.type.Number(n))C.call(h.container,n,r);else if(n instanceof e.Scene)n.controller()===d&&d.scrollTo(n.scrollOffset(),r);else if(i.type.Function(n))C=n;else{var o=i.get.elements(n)[0];if(o){for(;o.parentNode.hasAttribute(t);)o=o.parentNode;var s=h.vertical?"top":"left",a=i.get.offset(h.container),l=i.get.offset(o);
33911w||(a[s]-=d.scrollPos()),d.scrollTo(l[s]-a[s],r)}}return d},this.scrollPos=function(e){return arguments.length?(i.type.Function(e)&&(x=e),d):x.call(d)},this.info=function(e){var t={size:y,vertical:h.vertical,scrollPos:v,scrollDirection:m,container:h.container,isDocument:w};return arguments.length?void 0!==t[e]?t[e]:void 0:t},this.loglevel=function(){return d},this.enabled=function(e){return arguments.length?(S!=e&&(S=!!e,d.updateScene(g,!0)),d):S},this.destroy=function(e){window.clearTimeout(s);for(var t=g.length;t--;)g[t].destroy(e);return h.container.removeEventListener("resize",T),h.container.removeEventListener("scroll",T),i.cAF(o),null},b(),d};var n={defaults:{container:window,vertical:!0,globalSceneOptions:{},loglevel:2,refreshInterval:100}};e.Controller.addOption=function(e,t){n.defaults[e]=t},e.Controller.extend=function(t){var n=this;e.Controller=function(){return n.apply(this,arguments),this.$super=i.extend({},this),t.apply(this,arguments)||this},i.extend(e.Controller,n),e.Controller.prototype=n.prototype,e.Controller.prototype.constructor=e.Controller},e.Scene=function(n){var o,s,a="BEFORE",l="DURING",c="AFTER",u=r.defaults,f=this,d=i.extend({},u,n),h=a,g=0,p={start:0,end:0},v=0,m=!0,w=function(){for(var e in d)u.hasOwnProperty(e)||delete d[e];for(var t in u)L(t);C()},y={};this.on=function(e,t){return i.type.Function(t)&&(e=e.trim().split(" "),e.forEach(function(e){var n=e.split("."),r=n[0],i=n[1];"*"!=r&&(y[r]||(y[r]=[]),y[r].push({namespace:i||"",callback:t}))})),f},this.off=function(e,t){return e?(e=e.trim().split(" "),e.forEach(function(e){var n=e.split("."),r=n[0],i=n[1]||"",o="*"===r?Object.keys(y):[r];o.forEach(function(e){for(var n=y[e]||[],r=n.length;r--;){var o=n[r];!o||i!==o.namespace&&"*"!==i||t&&t!=o.callback||n.splice(r,1)}n.length||delete y[e]})}),f):f},this.trigger=function(t,n){if(t){var r=t.trim().split("."),i=r[0],o=r[1],s=y[i];s&&s.forEach(function(t){o&&o!==t.namespace||t.callback.call(f,new e.Event(i,t.namespace,f,n))})}return f},f.on("change.internal",function(e){"loglevel"!==e.what&&"tweenChanges"!==e.what&&("triggerElement"===e.what?E():"reverse"===e.what&&f.update())}).on("shift.internal",function(){S(),f.update()}),this.addTo=function(t){return t instanceof e.Controller&&s!=t&&(s&&s.removeScene(f),s=t,C(),b(!0),E(!0),S(),s.info("container").addEventListener("resize",x),t.addScene(f),f.trigger("add",{controller:s}),f.update()),f},this.enabled=function(e){return arguments.length?(m!=e&&(m=!!e,f.update(!0)),f):m},this.remove=function(){if(s){s.info("container").removeEventListener("resize",x);var e=s;s=void 0,e.removeScene(f),f.trigger("remove")}return f},this.destroy=function(e){return f.trigger("destroy",{reset:e}),f.remove(),f.off("*.*"),null},this.update=function(e){if(s)if(e)if(s.enabled()&&m){var t,n=s.info("scrollPos");t=d.duration>0?(n-p.start)/(p.end-p.start):n>=p.start?1:0,f.trigger("update",{startPos:p.start,endPos:p.end,scrollPos:n}),f.progress(t)}else T&&h===l&&O(!0);else s.updateScene(f,!1);return f},this.refresh=function(){return b(),E(),f},this.progress=function(e){if(arguments.length){var t=!1,n=h,r=s?s.info("scrollDirection"):"PAUSED",i=d.reverse||e>=g;if(0===d.duration?(t=g!=e,g=1>e&&i?0:1,h=0===g?a:l):0>e&&h!==a&&i?(g=0,h=a,t=!0):e>=0&&1>e&&i?(g=e,h=l,t=!0):e>=1&&h!==c?(g=1,h=c,t=!0):h!==l||i||O(),t){var o={progress:g,state:h,scrollDirection:r},u=h!=n,p=function(e){f.trigger(e,o)};u&&n!==l&&(p("enter"),p(n===a?"start":"end")),p("progress"),u&&h!==l&&(p(h===a?"start":"end"),p("leave"))}return f}return g};var S=function(){p={start:v+d.offset},s&&d.triggerElement&&(p.start-=s.info("size")*d.triggerHook),p.end=p.start+d.duration},b=function(e){if(o){var t="duration";F(t,o.call(f))&&!e&&(f.trigger("change",{what:t,newval:d[t]}),f.trigger("shift",{reason:t}))}},E=function(e){var n=0,r=d.triggerElement;if(s&&r){for(var o=s.info(),a=i.get.offset(o.container),l=o.vertical?"top":"left";r.parentNode.hasAttribute(t);)r=r.parentNode;var c=i.get.offset(r);o.isDocument||(a[l]-=s.scrollPos()),n=c[l]-a[l]}var u=n!=v;v=n,u&&!e&&f.trigger("shift",{reason:"triggerElementPosition"})},x=function(){d.triggerHook>0&&f.trigger("shift",{reason:"containerResize"})},z=i.extend(r.validate,{duration:function(e){if(i.type.String(e)&&e.match(/^(\.|\d)*\d+%$/)){var t=parseFloat(e)/100;e=function(){return s?s.info("size")*t:0}}if(i.type.Function(e)){o=e;try{e=parseFloat(o())}catch(t){e=-1}}if(e=parseFloat(e),!i.type.Number(e)||0>e)throw o?(o=void 0,0):0;return e}}),C=function(e){e=arguments.length?[e]:Object.keys(z),e.forEach(function(e){var t;if(z[e])try{t=z[e](d[e])}catch(n){t=u[e]}finally{d[e]=t}})},F=function(e,t){var n=!1,r=d[e];return d[e]!=t&&(d[e]=t,C(e),n=r!=d[e]),n},L=function(e){f[e]||(f[e]=function(t){return arguments.length?("duration"===e&&(o=void 0),F(e,t)&&(f.trigger("change",{what:e,newval:d[e]}),r.shifts.indexOf(e)>-1&&f.trigger("shift",{reason:e})),f):d[e]})};this.controller=function(){return s},this.state=function(){return h},this.scrollOffset=function(){return p.start},this.triggerPosition=function(){var e=d.offset;return s&&(e+=d.triggerElement?v:s.info("size")*f.triggerHook()),e};var T,A;f.on("shift.internal",function(e){var t="duration"===e.reason;(h===c&&t||h===l&&0===d.duration)&&O(),t&&_()}).on("progress.internal",function(){O()}).on("add.internal",function(){_()}).on("destroy.internal",function(e){f.removePin(e.reset)});var O=function(e){if(T&&s){var t=s.info(),n=A.spacer.firstChild;if(e||h!==l){var r={position:A.inFlow?"relative":"absolute",top:0,left:0},o=i.css(n,"position")!=r.position;A.pushFollowers?d.duration>0&&(h===c&&0===parseFloat(i.css(A.spacer,"padding-top"))?o=!0:h===a&&0===parseFloat(i.css(A.spacer,"padding-bottom"))&&(o=!0)):r[t.vertical?"top":"left"]=d.duration*g,i.css(n,r),o&&_()}else{"fixed"!=i.css(n,"position")&&(i.css(n,{position:"fixed"}),_());var u=i.get.offset(A.spacer,!0),f=d.reverse||0===d.duration?t.scrollPos-p.start:Math.round(g*d.duration*10)/10;u[t.vertical?"top":"left"]+=f,i.css(A.spacer.firstChild,{top:u.top,left
33911:u.left})}}},_=function(){if(T&&s&&A.inFlow){var e=h===l,t=s.info("vertical"),n=A.spacer.firstChild,r=i.isMarginCollapseType(i.css(A.spacer,"display")),o={};A.relSize.width||A.relSize.autoFullWidth?e?i.css(T,{width:i.get.width(A.spacer)}):i.css(T,{width:"100%"}):(o["min-width"]=i.get.width(t?T:n,!0,!0),o.width=e?o["min-width"]:"auto"),A.relSize.height?e?i.css(T,{height:i.get.height(A.spacer)-(A.pushFollowers?d.duration:0)}):i.css(T,{height:"100%"}):(o["min-height"]=i.get.height(t?n:T,!0,!r),o.height=e?o["min-height"]:"auto"),A.pushFollowers&&(o["padding"+(t?"Top":"Left")]=d.duration*g,o["padding"+(t?"Bottom":"Right")]=d.duration*(1-g)),i.css(A.spacer,o)}},N=function(){s&&T&&h===l&&!s.info("isDocument")&&O()},P=function(){s&&T&&h===l&&((A.relSize.width||A.relSize.autoFullWidth)&&i.get.width(window)!=i.get.width(A.spacer.parentNode)||A.relSize.height&&i.get.height(window)!=i.get.height(A.spacer.parentNode))&&_()},D=function(e){s&&T&&h===l&&!s.info("isDocument")&&(e.preventDefault(),s._setScrollPos(s.info("scrollPos")-((e.wheelDelta||e[s.info("vertical")?"wheelDeltaY":"wheelDeltaX"])/3||30*-e.detail)))};this.setPin=function(e,n){var r={pushFollowers:!0,spacerClass:"scrollmagic-pin-spacer"};if(n=i.extend({},r,n),e=i.get.elements(e)[0],!e)return f;if("fixed"===i.css(e,"position"))return f;if(T){if(T===e)return f;f.removePin()}T=e;var o=T.parentNode.style.display,s=["top","left","bottom","right","margin","marginLeft","marginRight","marginTop","marginBottom"];T.parentNode.style.display="none";var a="absolute"!=i.css(T,"position"),l=i.css(T,s.concat(["display"])),c=i.css(T,["width","height"]);T.parentNode.style.display=o,!a&&n.pushFollowers&&(n.pushFollowers=!1);var u=T.parentNode.insertBefore(document.createElement("div"),T),d=i.extend(l,{position:a?"relative":"absolute",boxSizing:"content-box",mozBoxSizing:"content-box",webkitBoxSizing:"content-box"});if(a||i.extend(d,i.css(T,["width","height"])),i.css(u,d),u.setAttribute(t,""),i.addClass(u,n.spacerClass),A={spacer:u,relSize:{width:"%"===c.width.slice(-1),height:"%"===c.height.slice(-1),autoFullWidth:"auto"===c.width&&a&&i.isMarginCollapseType(l.display)},pushFollowers:n.pushFollowers,inFlow:a},!T.___origStyle){T.___origStyle={};var h=T.style,g=s.concat(["width","height","position","boxSizing","mozBoxSizing","webkitBoxSizing"]);g.forEach(function(e){T.___origStyle[e]=h[e]||""})}return A.relSize.width&&i.css(u,{width:c.width}),A.relSize.height&&i.css(u,{height:c.height}),u.appendChild(T),i.css(T,{position:a?"relative":"absolute",margin:"auto",top:"auto",left:"auto",bottom:"auto",right:"auto"}),(A.relSize.width||A.relSize.autoFullWidth)&&i.css(T,{boxSizing:"border-box",mozBoxSizing:"border-box",webkitBoxSizing:"border-box"}),window.addEventListener("scroll",N),window.addEventListener("resize",N),window.addEventListener("resize",P),T.addEventListener("mousewheel",D),T.addEventListener("DOMMouseScroll",D),O(),f},this.removePin=function(e){if(T){if(h===l&&O(!0),e||!s){var n=A.spacer.firstChild;if(n.hasAttribute(t)){var r=A.spacer.style,o=["margin","marginLeft","marginRight","marginTop","marginBottom"];margins={},o.forEach(function(e){margins[e]=r[e]||""}),i.css(n,margins)}A.spacer.parentNode.insertBefore(n,A.spacer),A.spacer.parentNode.removeChild(A.spacer),T.parentNode.hasAttribute(t)||(i.css(T,T.___origStyle),delete T.___origStyle)}window.removeEventListener("scroll",N),window.removeEventListener("resize",N),window.removeEventListener("resize",P),T.removeEventListener("mousewheel",D),T.removeEventListener("DOMMouseScroll",D),T=void 0}return f};var R,k=[];return f.on("destroy.internal",function(e){f.removeClassToggle(e.reset)}),this.setClassToggle=function(e,t){var n=i.get.elements(e);return 0!==n.length&&i.type.String(t)?(k.length>0&&f.removeClassToggle(),R=t,k=n,f.on("enter.internal_class leave.internal_class",function(e){var t="enter"===e.type?i.addClass:i.removeClass;k.forEach(function(e){t(e,R)})}),f):f},this.removeClassToggle=function(e){return e&&k.forEach(function(e){i.removeClass(e,R)}),f.off("start.internal_class end.internal_class"),R=void 0,k=[],f},w(),f};var r={defaults:{duration:0,offset:0,triggerElement:void 0,triggerHook:.5,reverse:!0,loglevel:2},validate:{offset:function(e){if(e=parseFloat(e),!i.type.Number(e))throw 0;return e},triggerElement:function(e){if(e=e||void 0){var t=i.get.elements(e)[0];if(!t)throw 0;e=t}return e},triggerHook:function(e){var t={onCenter:.5,onEnter:1,onLeave:0};if(i.type.Number(e))e=Math.max(0,Math.min(parseFloat(e),1));else{if(!(e in t))throw 0;e=t[e]}return e},reverse:function(e){return!!e}},shifts:["duration","offset","triggerHook"]};e.Scene.addOption=function(e,t,n,i){e in r.defaults||(r.defaults[e]=t,r.validate[e]=n,i&&r.shifts.push(e))},e.Scene.extend=function(t){var n=this;e.Scene=function(){return n.apply(this,arguments),this.$super=i.extend({},this),t.apply(this,arguments)||this},i.extend(e.Scene,n),e.Scene.prototype=n.prototype,e.Scene.prototype.constructor=e.Scene},e.Event=function(e,t,n,r){r=r||{};for(var i in r)this[i]=r[i];return this.type=e,this.target=this.currentTarget=n,this.namespace=t||"",this.timeStamp=this.timestamp=Date.now(),this};var i=e._util=function(e){var t,n={},r=function(e){return parseFloat(e)||0},i=function(t){return t.currentStyle?t.currentStyle:e.getComputedStyle(t)}
33911,o=function(t,n,o,s){if(n=n===document?e:n,n===e)s=!1;else if(!f.DomElement(n))return 0;t=t.charAt(0).toUpperCase()+t.substr(1).toLowerCase();var a=(o?n["offset"+t]||n["outer"+t]:n["client"+t]||n["inner"+t])||0;if(o&&s){var l=i(n);a+="Height"===t?r(l.marginTop)+r(l.marginBottom):r(l.marginLeft)+r(l.marginRight)}return a},s=function(e){return e.replace(/^[^a-z]+([a-z])/g,"$1").replace(/-([a-z])/g,function(e){return e[1].toUpperCase()})};n.extend=function(e){for(e=e||{},t=1;t<arguments.length;t++)if(arguments[t])for(var n in arguments[t])arguments[t].hasOwnProperty(n)&&(e[n]=arguments[t][n]);return e},n.isMarginCollapseType=function(e){return["block","flex","list-item","table","-webkit-box"].indexOf(e)>-1};var a=0,l=["ms","moz","webkit","o"],c=e.requestAnimationFrame,u=e.cancelAnimationFrame;for(t=0;!c&&t<l.length;++t)c=e[l[t]+"RequestAnimationFrame"],u=e[l[t]+"CancelAnimationFrame"]||e[l[t]+"CancelRequestAnimationFrame"];c||(c=function(t){var n=(new Date).getTime(),r=Math.max(0,16-(n-a)),i=e.setTimeout(function(){t(n+r)},r);return a=n+r,i}),u||(u=function(t){e.clearTimeout(t)}),n.rAF=c.bind(e),n.cAF=u.bind(e);var f=n.type=function(e){return Object.prototype.toString.call(e).replace(/^\[object (.+)\]$/,"$1").toLowerCase()};f.String=function(e){return"string"===f(e)},f.Function=function(e){return"function"===f(e)},f.Array=function(e){return Array.isArray(e)},f.Number=function(e){return!f.Array(e)&&e-parseFloat(e)+1>=0},f.DomElement=function(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:e&&"object"==typeof e&&null!==e&&1===e.nodeType&&"string"==typeof e.nodeName};var d=n.get={};return d.elements=function(t){var n=[];if(f.String(t))try{t=document.querySelectorAll(t)}catch(e){return n}if("nodelist"===f(t)||f.Array(t))for(var r=0,i=n.length=t.length;i>r;r++){var o=t[r];n[r]=f.DomElement(o)?o:d.elements(o)}else(f.DomElement(t)||t===document||t===e)&&(n=[t]);return n},d.scrollTop=function(t){return t&&"number"==typeof t.scrollTop?t.scrollTop:e.pageYOffset||0},d.scrollLeft=function(t){return t&&"number"==typeof t.scrollLeft?t.scrollLeft:e.pageXOffset||0},d.width=function(e,t,n){return o("width",e,t,n)},d.height=function(e,t,n){return o("height",e,t,n)},d.offset=function(e,t){var n={top:0,left:0};if(e&&e.getBoundingClientRect){var r=e.getBoundingClientRect();n.top=r.top,n.left=r.left,t||(n.top+=d.scrollTop(),n.left+=d.scrollLeft())}return n},n.addClass=function(e,t){t&&(e.classList?e.classList.add(t):e.className+=" "+t)},n.removeClass=function(e,t){t&&(e.classList?e.classList.remove(t):e.className=e.className.replace(RegExp("(^|\\b)"+t.split(" ").join("|")+"(\\b|$)","gi")," "))},n.css=function(e,t){if(f.String(t))return i(e)[s(t)];if(f.Array(t)){var n={},r=i(e);return t.forEach(function(e){n[e]=r[s(e)]}),n}for(var o in t){var a=t[o];a==parseFloat(a)&&(a+="px"),e.style[s(o)]=a}},n}(window||{});return e}); 33912 33913(function(){ 33914 33915 var videoPlayerOverlay = function () { 33916 33917 //getparameter by name (needed to get the youtube id - non-shared) 33918 function getParameterByName(name, url) { 33919 ââââif (!url) url = window.location.href; 33920 ââââname = name.replace(/[\[\]]/g, "\\$&"); 33921 ââââvar regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)", "i"), 33922 ââââââââresults = regex.exec(url); 33923 ââââif (!results) return null; 33924 ââââif (!results[2]) return ''; 33925 ââââreturn decodeURIComponent(results[2].replace(/\+/g, " ")); 33926 } 33927 33928 //Open video overlay............................................. 33929 $(".open-video-overlay, .rte-open-video-overlay a").click(function (event) { 33930 event.preventDefault(); 33931 33932 var videoUrl = $(this).attr("href"); 33933 var sharedYouTubeLink = "https://youtu.be/"; 33934 var vimeoLink = "https://vimeo.com/"; 33935 var videoId; 33936 var embedUrl; 33937 33938 if(videoUrl.indexOf(vimeoLink) > -1) { 33939 videoId = videoUrl.replace(vimeoLink, ''); 33940 var embedUrl = 'https://player.vimeo.com/video/' + videoId + '?autoplay=1" width="500" height="300" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen'; 33941 } 33942 else if(videoUrl.indexOf(sharedYouTubeLink) > -1) { 33943 videoId = videoUrl.replace(sharedYouTubeLink, ''); 33944 embedUrl = '//www.youtube.com/embed/' + videoId + '?enablejsapi=1&rel=0&autoplay=1&showinfo=0&wmode=transparent'; 33945 } 33946 else { 33947 videoId = getParameterByName('v', videoUrl); 33948 embedUrl = '//www.youtube.com/embed/' + videoId + '?enablejsapi=1&rel=0&autoplay=1&showinfo=0&wmode=transparent'; 33949 } 33950 33951 //Embed iframe source + open overlay 33952 $("#fullpage-player").attr("src", embedUrl); 33953 $("html").addClass("fullpage-overlay-opened"); 33954 $("#top").addClass("top-slim-header"); 33955 33956 }); 33957 33958 //Close video overlay............................................ 33959 $("#close-fullpage-overlay").click(function () { 33960 $("html").removeClass("fullpage-overlay-opened"); 33961 $("#top").removeClass("top-slim-header"); 33962 setTimeout(function(){ 33963 $("#fullpage-player").attr("src", "about:blank"); 33964 }, 1000); 33965 }); 33966 } 33967 33968 var videoPlayer = function () { 33969 33970 $(".video-overlay-image").click(function () { 33971 33972 var parentContainer = $(this).parents(".video-container"); 33973 var thisVideo = parentContainer.find(".video-id").html(); 33974 var wrapper = $(this).parents(".module-video"); 33975 33976 if ( parentContainer.find( ".vimeo-video-container" ).length ) { 33977 var embedUrl = 'https://player.vimeo.com/video/' + thisVideo + '?autoplay=1" width="500" height="300" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen'; 33978 33979 if($(wrapper).is(".show-in-overlay")) { 33980 $('#fullpage-player').attr('src', embedUrl); 33981 $('html').addClass('fullpage-overlay-opened'); 33982 $('body').addClass('top-slim-header'); 33983 } 33984 else { 33985 parentContainer.find(".video-overlay-image").fadeOut(); 33986 parentContainer.find(".video-id").html('<iframe src="' + embedUrl + '"></iframe>'); 33987 } 33988 } 33989 else { 33990 var embedUrl = '//www.youtube.com/embed/' + thisVideo + '?enablejsapi=1&rel=0&autoplay=1&showinfo=0&wmode=transparent'; 33991 33992 if($(wrapper).is(".show-in-overlay")) { 33993 $('#fullpage-player').attr('src', embedUrl); 33994 $('html').addClass('fullpage-overlay-opened'); 33995 $('body').addClass('top-slim-header'); 33996 } 33997 else { 33998 parentContainer.find('.video-overlay-image').fadeOut(); 33999 parentContainer.find(".video-id").html('<iframe src="' + embedUrl + '"></iframe>'); 34000 } 34001 } 34002 34003 }); 34004 } 34005 34006 $(document).ready(function() { 34007 videoPlayer(); 34008 videoPlayerOverlay(); 34009 });
34010 34011})(); 34012 34013var app = angular.module('HeydayThemeApp', ['ngAnimate']).constant('BASE_API_URI', '/umbraco/api/'); 34014app.controller('DistributorsController', ["$scope", function ($scope) { 34015 var formOpened = false; 34016 $scope.enyDistributor = function () { 34017 if ($scope.country == "default") { 34018 return false; 34019 } 34020 var result = true; 34021 $.each($scope.distributors, function () { 34022 if (this.Country == $scope.country) { 34023 result = false; 34024 return result; 34025 } 34026 }); 34027 if (result == true && formOpened == false) { 34028 formOpened = true; 34029 $(".open-contact-form").trigger("click"); 34030 } 34031 return result; 34032 }; 34033}]); 34034//# sourceMappingURL=app.js.map 34035app.factory('Category', function ($http, BASE_API_URI) { 34036 return { 34037 getProducts: function () { 34038 var params = { 34039 lang: $("#lang").val() 34040 }; 34041 return $http.post(BASE_API_URI + "Products/GetProducts", params); 34042 } 34043 }; 34044}); 34045//# sourceMappingURL=category.js.map 34046app.controller('CategoryController', ["$scope", "Category", "$animate", function ($scope, service, $animate) { 34047 $scope.filter = filter; 34048 $scope.products = null; 34049 service.getProducts().then(function (response) { 34050 $scope.products = response.data; 34051 }); 34052 $scope.$watch('filter', function () { 34053 }, true); 34054 function isFilterActive(filter) { 34055 return _.isEmpty(_.filter(filter, _.identity)); 34056 } 34057 function inArray(item, array) { 34058 var result = false; 34059 $.each(array, function () { 34060 if (item[this] == true) { 34061 result = true; 34062 } 34063 }); 34064 return result; 34065 } 34066 $scope.hasExtraProducts = function () { 34067 var value = false; 34068 if ($scope.products != null) { 34069 $.each($scope.products, function () { 34070 if (this.isExtra && $scope.visible(this)) { 34071 value = true; 34072 return; 34073 } 34074 }); 34075 } 34076 return value; 34077 }; 34078 $scope.enyProducts = function () { 34079 var result = false; 34080 $.each($scope.products, function () { 34081 if ($scope.visible(this)) { 34082 result = true; 34083 return result; 34084 } 34085 }); 34086 return result; 34087 }; 34088 var isEnyCategorySelected = function (filter) { 34089 var result = false; 34090 _.each(filter, function (item) { 34091 if (item) { 34092 result = true; 34093 } 34094 }); 34095 return result; 34096 }; 34097 $scope.visible = function (product) { 34098 if (product != undefined) { 34099 var result = true; 34100 $.each($scope.filter, function (index) { 34101 var filter = this; 34102 if (index == "category") { 34103 if (isFilterActive($scope.filter.category) || $scope.filter.category[product.categoryId]) { 34104 } 34105 else { 34106 result = false; 34107 } 34108 } 34109 else if ((Object.getOwnPropertyNames(filter).length == 0 || !isEnyCategorySelected(filter)) && result == true || inArray(filter, product.categories) && result == true) { 34110 } 34111 else { 34112 result = false; 34113 } 34114 }); 34115 return result; 34116 } 34117 }; 34118}]); 34119//# sourceMappingURL=category.js.map 34120/** 34121 * Owl Carousel v2.2.1 34122 * Copyright 2013-2017 David Deutsch 34123 * Licensed under () 34124 */ 34125/** 34126 * Owl carousel 34127 * @version 2.1.6 34128 * @author Bartosz Wojciechowski 34129 * @author David Deutsch 34130 * @license The MIT License (MIT) 34131 * @todo Lazy Load Icon 34132 * @todo prevent animationend bubling 34133 * @todo itemsScaleUp 34134 * @todo Test Zepto 34135 * @todo stagePadding calculate wrong active classes 34136 */ 34137;(function($, window, document, undefined) { 34138 34139 /** 34140 * Creates a carousel. 34141 * @class The Owl Carousel. 34142 * @public 34143 * @param {HTMLElement|jQuery} element - The element to create the carousel for. 34144 * @param {Object} [options] - The options 34145 */ 34146 function Owl(element, options) { 34147 34148 /** 34149 * Current settings for the carousel. 34150 * @public 34151 */ 34152 this.settings = null; 34153 34154 /** 34155 * Current options set by the caller including defaults. 34156 * @public 34157 */ 34158 this.options = $.extend({}, Owl.Defaults, options); 34159 34160 /** 34161 * Plugin element. 34162 * @public 34163 */ 34164 this.$element = $(element); 34165 34166 /** 34167 * Proxied event handlers. 34168 * @protected 34169 */ 34170 this._handlers = {}; 34171 34172 /** 34173 * References to the running plugins of this carousel. 34174 * @protected 34175 */ 34176 this._plugins = {}; 34177 34178 /** 34179 * Currently suppressed events to prevent them from beeing retriggered. 34180 * @protected 34181 */ 34182 this._supress = {}; 34183 34184 /** 34185 * Absolute current position. 34186 * @protected 34187 */ 34188 this._current = null; 34189 34190 /** 34191 * Animation speed in milliseconds. 34192 * @protected 34193 */ 34194 this._speed = null; 34195 34196 /** 34197 * Coordinates of all items in pixel. 34198 * @todo The name of this member is missleading. 34199 * @protected 34200 */ 34201 this._coordinates = []; 34202 34203 /** 34204 * Current breakpoint. 34205 * @todo Real media queries would be nice. 34206 * @protected 34207 */ 34208 this._breakpoint = null; 34209 34210 /** 34211 * Current width of the plugin element. 34212 */ 34213 this._width = null; 34214 34215 /** 34216 * All real items. 34217 * @protected 34218 */ 34219 this._items = []; 34220 34221 /** 34222 * All cloned items. 34223 * @protected 34224 */ 34225 this._clones = []; 34226 34227 /** 34228 * Merge values of all items. 34229 * @todo Maybe this could be part of a plugin. 34230 * @protected 34231 */ 34232 this._mergers = []; 34233 34234 /** 34235 * Widths of all items. 34236 */ 34237 this._widths = []; 34238 34239 /** 34240 * Invalidated parts within the update process. 34241 * @protected 34242 */ 34243 this._invalidated = {}; 34244 34245 /** 34246 * Ordered list of workers for the update process. 34247 * @protected 34248 */ 34249 this._pipe = []; 34250 34251 /** 34252 * Current state information for the drag operation. 34253 * @todo #261 34254 * @protected 34255 */ 34256 this._drag = { 34257 time: null, 34258 target: null, 34259 pointer: null, 34260 stage: { 34261 start: null, 34262 current: null 34263 }, 34264 direction: null 34265 }; 34266 34267 /** 34268 * Current state information and their tags. 34269 * @type {Object} 34270 * @protected 34271 */ 34272 this._states = { 34273 current: {}, 34274 tags: { 34275 'initializing': [ 'busy' ], 34276 'animating': [ 'busy' ], 34277 'dragging': [ 'interacting' ] 34278 } 34279 }; 34280 34281 $.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) { 34282 this._handlers[handler] = $.proxy(this[handler], this); 34283 }, this)); 34284 34285 $.each(Owl.Plugins, $.proxy(function(key, plugin) { 34286 this._plugins[key.charAt(0).toLowerCase() + key.slice(1)] 34287 = new plugin(this); 34288 }, this)); 34289 34290 $.each(Owl.Workers, $.proxy(function(priority, worker) { 34291 this._pipe.push({ 34292 'filter': worker.filter, 34293 'run': $.proxy(worker.run, this) 34294 }); 34295 }, this)); 34296 34297 this.setup(); 34298 this.initialize(); 34299 } 34300 34301 /** 34302 * Default options for the carousel. 34303 * @public 34304 */ 34305 Owl.Defaults = { 34306 items: 3, 34307 loop: false, 34308 center: false, 34309 rewind: false, 34310
vendor: 4,168 bytes, lines 34311-34485
34311 mouseDrag: true, 34312 touchDrag: true, 34313 pullDrag: true, 34314 freeDrag: false, 34315 34316 margin: 0, 34317 stagePadding: 0, 34318 34319 merge: false, 34320 mergeFit: true, 34321 autoWidth: false, 34322 34323 startPosition: 0, 34324 rtl: false, 34325 34326 smartSpeed: 250, 34327 fluidSpeed: false, 34328 dragEndSpeed: false, 34329 34330 responsive: {}, 34331 responsiveRefreshRate: 200, 34332 responsiveBaseElement: window, 34333 34334 fallbackEasing: 'swing', 34335 34336 info: false, 34337 34338 nestedItemSelector: false, 34339 itemElement: 'div', 34340 stageElement: 'div', 34341 34342 refreshClass: 'owl-refresh', 34343 loadedClass: 'owl-loaded', 34344 loadingClass: 'owl-loading', 34345 rtlClass: 'owl-rtl', 34346 responsiveClass: 'owl-responsive', 34347 dragClass: 'owl-drag', 34348 itemClass: 'owl-item', 34349 stageClass: 'owl-stage', 34350 stageOuterClass: 'owl-stage-outer', 34351 grabClass: 'owl-grab' 34352 }; 34353 34354 /** 34355 * Enumeration for width. 34356 * @public 34357 * @readonly 34358 * @enum {String} 34359 */ 34360 Owl.Width = { 34361 Default: 'default', 34362 Inner: 'inner', 34363 Outer: 'outer' 34364 }; 34365 34366 /** 34367 * Enumeration for types. 34368 * @public 34369 * @readonly 34370 * @enum {String} 34371 */ 34372 Owl.Type = { 34373 Event: 'event', 34374 State: 'state' 34375 }; 34376 34377 /** 34378 * Contains all registered plugins. 34379 * @public 34380 */ 34381 Owl.Plugins = {}; 34382 34383 /** 34384 * List of workers involved in the update process. 34385 */ 34386 Owl.Workers = [ { 34387 filter: [ 'width', 'settings' ], 34388 run: function() { 34389 this._width = this.$element.width(); 34390 } 34391 }, { 34392 filter: [ 'width', 'items', 'settings' ], 34393 run: function(cache) { 34394 cache.current = this._items && this._items[this.relative(this._current)]; 34395 } 34396 }, { 34397 filter: [ 'items', 'settings' ], 34398 run: function() { 34399 this.$stage.children('.cloned').remove(); 34400 } 34401 }, { 34402 filter: [ 'width', 'items', 'settings' ], 34403 run: function(cache) { 34404 var margin = this.settings.margin || '', 34405 grid = !this.settings.autoWidth, 34406 rtl = this.settings.rtl, 34407 css = { 34408 'width': 'auto', 34409 'margin-left': rtl ? margin : '', 34410 'margin-right': rtl ? '' : margin 34411 }; 34412 34413 !grid && this.$stage.children().css(css); 34414 34415 cache.css = css; 34416 } 34417 }, { 34418 filter: [ 'width', 'items', 'settings' ], 34419 run: function(cache) { 34420 var width = (this.width() / this.settings.items).toFixed(3) - this.settings.margin, 34421 merge = null, 34422 iterator = this._items.length, 34423 grid = !this.settings.autoWidth, 34424 widths = []; 34425 34426 cache.items = { 34427 merge: false, 34428 width: width 34429 }; 34430 34431 while (iterator--) { 34432 merge = this._mergers[iterator]; 34433 merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge; 34434 34435 cache.items.merge = merge > 1 || cache.items.merge; 34436 34437 widths[iterator] = !grid ? this._items[iterator].width() : width * merge; 34438 } 34439 34440 this._widths = widths; 34441 } 34442 }, { 34443 filter: [ 'items', 'settings' ], 34444 run: function() { 34445 var clones = [], 34446 items = this._items, 34447 settings = this.settings, 34448 // TODO: Should be computed from number of min width items in stage 34449 view = Math.max(settings.items * 2, 4), 34450 size = Math.ceil(items.length / 2) * 2, 34451 repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0, 34452 append = '', 34453 prepend = ''; 34454 34455 repeat /= 2; 34456 34457 while (repeat--) { 34458 // Switch to only using appended clones 34459 clones.push(this.normalize(clones.length / 2, true)); 34460 append = append + items[clones[clones.length - 1]][0].outerHTML; 34461 clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true)); 34462 prepend = items[clones[clones.length - 1]][0].outerHTML + prepend; 34463 } 34464 34465 this._clones = clones; 34466 34467 $(append).addClass('cloned').appendTo(this.$stage); 34468 $(prepend).addClass('cloned').prependTo(this.$stage); 34469 } 34470 }, { 34471 filter: [ 'width', 'items', 'settings' ], 34472 run: function() { 34473 var rtl = this.settings.rtl ? 1 : -1, 34474 size = this._clones.length + this._items.length, 34475 iterator = -1, 34476 previous = 0, 34477 current = 0, 34478 coordinates = []; 34479 34480 while (++iterator < size) { 34481 previous = coordinates[iterator - 1] || 0; 34482 current = this._widths[this.relative(iterator)] + this.settings.margin; 34483 coordinates.push(previous + current * rtl); 34484 } 34485
vendor: 8,251 bytes, lines 34486-34797
34486 this._coordinates = coordinates; 34487 } 34488 }, { 34489 filter: [ 'width', 'items', 'settings' ], 34490 run: function() { 34491 var padding = this.settings.stagePadding, 34492 coordinates = this._coordinates, 34493 css = { 34494 'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2, 34495 'padding-left': padding || '', 34496 'padding-right': padding || '' 34497 }; 34498 34499 this.$stage.css(css); 34500 } 34501 }, { 34502 filter: [ 'width', 'items', 'settings' ], 34503 run: function(cache) { 34504 var iterator = this._coordinates.length, 34505 grid = !this.settings.autoWidth, 34506 items = this.$stage.children(); 34507 34508 if (grid && cache.items.merge) { 34509 while (iterator--) { 34510 cache.css.width = this._widths[this.relative(iterator)]; 34511 items.eq(iterator).css(cache.css); 34512 } 34513 } else if (grid) { 34514 cache.css.width = cache.items.width; 34515 items.css(cache.css); 34516 } 34517 } 34518 }, { 34519 filter: [ 'items' ], 34520 run: function() { 34521 this._coordinates.length < 1 && this.$stage.removeAttr('style'); 34522 } 34523 }, { 34524 filter: [ 'width', 'items', 'settings' ], 34525 run: function(cache) { 34526 cache.current = cache.current ? this.$stage.children().index(cache.current) : 0; 34527 cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current)); 34528 this.reset(cache.current); 34529 } 34530 }, { 34531 filter: [ 'position' ], 34532 run: function() { 34533 this.animate(this.coordinates(this._current)); 34534 } 34535 }, { 34536 filter: [ 'width', 'position', 'items', 'settings' ], 34537 run: function() { 34538 var rtl = this.settings.rtl ? 1 : -1, 34539 padding = this.settings.stagePadding * 2, 34540 begin = this.coordinates(this.current()) + padding, 34541 end = begin + this.width() * rtl, 34542 inner, outer, matches = [], i, n; 34543 34544 for (i = 0, n = this._coordinates.length; i < n; i++) { 34545 inner = this._coordinates[i - 1] || 0; 34546 outer = Math.abs(this._coordinates[i]) + padding * rtl; 34547 34548 if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end))) 34549 || (this.op(outer, '<', begin) && this.op(outer, '>', end))) { 34550 matches.push(i); 34551 } 34552 } 34553 34554 this.$stage.children('.active').removeClass('active'); 34555 this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active'); 34556 34557 if (this.settings.center) { 34558 this.$stage.children('.center').removeClass('center'); 34559 this.$stage.children().eq(this.current()).addClass('center'); 34560 } 34561 } 34562 } ]; 34563 34564 /** 34565 * Initializes the carousel. 34566 * @protected 34567 */ 34568 Owl.prototype.initialize = function() { 34569 this.enter('initializing'); 34570 this.trigger('initialize'); 34571 34572 this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl); 34573 34574 if (this.settings.autoWidth && !this.is('pre-loading')) { 34575 var imgs, nestedSelector, width; 34576 imgs = this.$element.find('img'); 34577 nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined; 34578 width = this.$element.children(nestedSelector).width(); 34579 34580 if (imgs.length && width <= 0) { 34581 this.preloadAutoWidthImages(imgs); 34582 } 34583 } 34584 34585 this.$element.addClass(this.options.loadingClass); 34586 34587 // create stage 34588 this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>') 34589 .wrap('<div class="' + this.settings.stageOuterClass + '"/>'); 34590 34591 // append stage 34592 this.$element.append(this.$stage.parent()); 34593 34594 // append content 34595 this.replace(this.$element.children().not(this.$stage.parent())); 34596 34597 // check visibility 34598 if (this.$element.is(':visible')) { 34599 // update view 34600 this.refresh(); 34601 } else { 34602 // invalidate width 34603 this.invalidate('width'); 34604 } 34605 34606 this.$element 34607 .removeClass(this.options.loadingClass) 34608 .addClass(this.options.loadedClass); 34609 34610 // register event handlers 34611 this.registerEventHandlers(); 34612 34613 this.leave('initializing'); 34614 this.trigger('initialized'); 34615 }; 34616 34617 /** 34618 * Setups the current settings. 34619 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8? 34620 * @todo Support for media queries by using `matchMedia` would be nice. 34621 * @public 34622 */ 34623 Owl.prototype.setup = function() { 34624 var viewport = this.viewport(), 34625 overwrites = this.options.responsive, 34626 match = -1, 34627 settings = null; 34628 34629 if (!overwrites) { 34630 settings = $.extend({}, this.options); 34631 } else { 34632 $.each(overwrites, function(breakpoint) { 34633 if (breakpoint <= viewport && breakpoint > match) { 34634 match = Number(breakpoint); 34635 } 34636 }); 34637 34638 settings = $.extend({}, this.options, overwrites[match]); 34639 if (typeof settings.stagePadding === 'function') { 34640 settings.stagePadding = settings.stagePadding(); 34641 } 34642 delete settings.responsive; 34643 34644 // responsive class 34645 if (settings.responsiveClass) { 34646 this.$element.attr('class', 34647 this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match) 34648 ); 34649 } 34650 } 34651 34652 this.trigger('change', { property: { name: 'settings', value: settings } }); 34653 this._breakpoint = match; 34654 this.settings = settings; 34655 this.invalidate('settings'); 34656 this.trigger('changed', { property: { name: 'settings', value: this.settings } }); 34657 }; 34658 34659 /** 34660 * Updates option logic if necessery. 34661 * @protected 34662 */ 34663 Owl.prototype.optionsLogic = function() { 34664 if (this.settings.autoWidth) { 34665 this.settings.stagePadding = false; 34666 this.settings.merge = false; 34667 } 34668 }; 34669 34670 /** 34671 * Prepares an item before add. 34672 * @todo Rename event parameter `content` to `item`. 34673 * @protected 34674 * @returns {jQuery|HTMLElement} - The item container. 34675 */ 34676 Owl.prototype.prepare = function(item) { 34677 var event = this.trigger('prepare', { content: item }); 34678 34679 if (!event.data) { 34680 event.data = $('<' + this.settings.itemElement + '/>') 34681 .addClass(this.options.itemClass).append(item) 34682 } 34683 34684 this.trigger('prepared', { content: event.data }); 34685 34686 return event.data; 34687 }; 34688 34689 /** 34690 * Updates the view. 34691 * @public 34692 */ 34693 Owl.prototype.update = function() { 34694 var i = 0, 34695 n = this._pipe.length, 34696 filter = $.proxy(function(p) { return this[p] }, this._invalidated), 34697 cache = {}; 34698 34699 while (i < n) { 34700 if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) { 34701 this._pipe[i].run(cache); 34702 } 34703 i++; 34704 } 34705 34706 this._invalidated = {}; 34707 34708 !this.is('valid') && this.enter('valid'); 34709 }; 34710 34711 /** 34712 * Gets the width of the view. 34713 * @public 34714 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return. 34715 * @returns {Number} - The width of the view in pixel. 34716 */ 34717 Owl.prototype.width = function(dimension) { 34718 dimension = dimension || Owl.Width.Default; 34719 switch (dimension) { 34720 case Owl.Width.Inner: 34721 case Owl.Width.Outer: 34722 return this._width; 34723 default: 34724 return this._width - this.settings.stagePadding * 2 + this.settings.margin; 34725 } 34726 }; 34727 34728 /** 34729 * Refreshes the carousel primarily for adaptive purposes. 34730 * @public 34731 */ 34732 Owl.prototype.refresh = function() { 34733 this.enter('refreshing'); 34734 this.trigger('refresh'); 34735 34736 this.setup(); 34737 34738 this.optionsLogic(); 34739 34740 this.$element.addClass(this.options.refreshClass); 34741 34742 this.update(); 34743 34744 this.$element.removeClass(this.options.refreshClass); 34745 34746 this.leave('refreshing'); 34747 this.trigger('refreshed'); 34748 }; 34749 34750 /** 34751 * Checks window `resize` event. 34752 * @protected 34753 */ 34754 Owl.prototype.onThrottledResize = function() { 34755 window.clearTimeout(this.resizeTimer); 34756 this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate); 34757 }; 34758 34759 /** 34760 * Checks window `resize` event. 34761 * @protected 34762 */ 34763 Owl.prototype.onResize = function() { 34764 if (!this._items.length) { 34765 return false; 34766 } 34767 34768 if (this._width === this.$element.width()) { 34769 return false; 34770 } 34771 34772 if (!this.$element.is(':visible')) { 34773 return false; 34774 } 34775 34776 this.enter('resizing'); 34777 34778 if (this.trigger('resize').isDefaultPrevented()) { 34779 this.leave('resizing'); 34780 return false; 34781 } 34782 34783 this.invalidate('width'); 34784 34785 this.refresh(); 34786 34787 this.leave('resizing'); 34788 this.trigger('resized'); 34789 }; 34790 34791 /** 34792 * Registers event handlers. 34793 * @todo Check `msPointerEnabled` 34794 * @todo #261 34795 * @protected 34796 */ 34797 Owl.prototype.registerEventHandlers = function() {
vendor: 5,214 bytes, lines 34798-34965
34798 if ($.support.transition) { 34799 this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this)); 34800 } 34801 34802 if (this.settings.responsive !== false) { 34803 this.on(window, 'resize', this._handlers.onThrottledResize); 34804 } 34805 34806 if (this.settings.mouseDrag) { 34807 this.$element.addClass(this.options.dragClass); 34808 this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this)); 34809 this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false }); 34810 } 34811 34812 if (this.settings.touchDrag){ 34813 this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this)); 34814 this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this)); 34815 } 34816 }; 34817 34818 /** 34819 * Handles `touchstart` and `mousedown` events. 34820 * @todo Horizontal swipe threshold as option 34821 * @todo #261 34822 * @protected 34823 * @param {Event} event - The event arguments. 34824 */ 34825 Owl.prototype.onDragStart = function(event) { 34826 var stage = null; 34827 34828 if (event.which === 3) { 34829 return; 34830 } 34831 34832 if ($.support.transform) { 34833 stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(','); 34834 stage = { 34835 x: stage[stage.length === 16 ? 12 : 4], 34836 y: stage[stage.length === 16 ? 13 : 5] 34837 }; 34838 } else { 34839 stage = this.$stage.position(); 34840 stage = { 34841 x: this.settings.rtl ? 34842 stage.left + this.$stage.width() - this.width() + this.settings.margin : 34843 stage.left, 34844 y: stage.top 34845 }; 34846 } 34847 34848 if (this.is('animating')) { 34849 $.support.transform ? this.animate(stage.x) : this.$stage.stop() 34850 this.invalidate('position'); 34851 } 34852 34853 this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown'); 34854 34855 this.speed(0); 34856 34857 this._drag.time = new Date().getTime(); 34858 this._drag.target = $(event.target); 34859 this._drag.stage.start = stage; 34860 this._drag.stage.current = stage; 34861 this._drag.pointer = this.pointer(event); 34862 34863 $(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this)); 34864 34865 $(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) { 34866 var delta = this.difference(this._drag.pointer, this.pointer(event)); 34867 34868 $(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this)); 34869 34870 if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) { 34871 return; 34872 } 34873 34874 event.preventDefault(); 34875 34876 this.enter('dragging'); 34877 this.trigger('drag'); 34878 }, this)); 34879 }; 34880 34881 /** 34882 * Handles the `touchmove` and `mousemove` events. 34883 * @todo #261 34884 * @protected 34885 * @param {Event} event - The event arguments. 34886 */ 34887 Owl.prototype.onDragMove = function(event) { 34888 var minimum = null, 34889 maximum = null, 34890 pull = null, 34891 delta = this.difference(this._drag.pointer, this.pointer(event)), 34892 stage = this.difference(this._drag.stage.start, delta); 34893 34894 if (!this.is('dragging')) { 34895 return; 34896 } 34897 34898 event.preventDefault(); 34899 34900 if (this.settings.loop) { 34901 minimum = this.coordinates(this.minimum()); 34902 maximum = this.coordinates(this.maximum() + 1) - minimum; 34903 stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum; 34904 } else { 34905 minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum()); 34906 maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum()); 34907 pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0; 34908 stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull); 34909 } 34910 34911 this._drag.stage.current = stage; 34912 34913 this.animate(stage.x); 34914 }; 34915 34916 /** 34917 * Handles the `touchend` and `mouseup` events. 34918 * @todo #261 34919 * @todo Threshold for click event 34920 * @protected 34921 * @param {Event} event - The event arguments. 34922 */ 34923 Owl.prototype.onDragEnd = function(event) { 34924 var delta = this.difference(this._drag.pointer, this.pointer(event)), 34925 stage = this._drag.stage.current, 34926 direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right'; 34927 34928 $(document).off('.owl.core'); 34929 34930 this.$element.removeClass(this.options.grabClass); 34931 34932 if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) { 34933 this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed); 34934 this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction)); 34935 this.invalidate('position'); 34936 this.update(); 34937 34938 this._drag.direction = direction; 34939 34940 if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) { 34941 this._drag.target.one('click.owl.core', function() { return false; }); 34942 } 34943 } 34944 34945 if (!this.is('dragging')) { 34946 return; 34947 } 34948 34949 this.leave('dragging'); 34950 this.trigger('dragged'); 34951 }; 34952 34953 /** 34954 * Gets absolute position of the closest item for a coordinate. 34955 * @todo Setting `freeDrag` makes `closest` not reusable. See #165. 34956 * @protected 34957 * @param {Number} coordinate - The coordinate in pixel. 34958 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`. 34959 * @return {Number} - The absolute position of the closest item. 34960 */ 34961 Owl.prototype.closest = function(coordinate, direction) { 34962 var position = -1, 34963 pull = 30, 34964 width = this.width(), 34965 coordinates = this.coordinates();
vendor: 59,760 bytes, lines 34966-37108
34966 34967 if (!this.settings.freeDrag) { 34968 // check closest item 34969 $.each(coordinates, $.proxy(function(index, value) { 34970 // on a left pull, check on current index 34971 if (direction === 'left' && coordinate > value - pull && coordinate < value + pull) { 34972 position = index; 34973 // on a right pull, check on previous index 34974 // to do so, subtract width from value and set position = index + 1 34975 } else if (direction === 'right' && coordinate > value - width - pull && coordinate < value - width + pull) { 34976 position = index + 1; 34977 } else if (this.op(coordinate, '<', value) 34978 && this.op(coordinate, '>', coordinates[index + 1] || value - width)) { 34979 position = direction === 'left' ? index + 1 : index; 34980 } 34981 return position === -1; 34982 }, this)); 34983 } 34984 34985 if (!this.settings.loop) { 34986 // non loop boundries 34987 if (this.op(coordinate, '>', coordinates[this.minimum()])) { 34988 position = coordinate = this.minimum(); 34989 } else if (this.op(coordinate, '<', coordinates[this.maximum()])) { 34990 position = coordinate = this.maximum(); 34991 } 34992 } 34993 34994 return position; 34995 }; 34996 34997 /** 34998 * Animates the stage. 34999 * @todo #270 35000 * @public 35001 * @param {Number} coordinate - The coordinate in pixels. 35002 */ 35003 Owl.prototype.animate = function(coordinate) { 35004 var animate = this.speed() > 0; 35005 35006 this.is('animating') && this.onTransitionEnd(); 35007 35008 if (animate) { 35009 this.enter('animating'); 35010 this.trigger('translate'); 35011 } 35012 35013 if ($.support.transform3d && $.support.transition) { 35014 this.$stage.css({ 35015 transform: 'translate3d(' + coordinate + 'px,0px,0px)', 35016 transition: (this.speed() / 1000) + 's' 35017 }); 35018 } else if (animate) { 35019 this.$stage.animate({ 35020 left: coordinate + 'px' 35021 }, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this)); 35022 } else { 35023 this.$stage.css({ 35024 left: coordinate + 'px' 35025 }); 35026 } 35027 }; 35028 35029 /** 35030 * Checks whether the carousel is in a specific state or not. 35031 * @param {String} state - The state to check. 35032 * @returns {Boolean} - The flag which indicates if the carousel is busy. 35033 */ 35034 Owl.prototype.is = function(state) { 35035 return this._states.current[state] && this._states.current[state] > 0; 35036 }; 35037 35038 /** 35039 * Sets the absolute position of the current item. 35040 * @public 35041 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged. 35042 * @returns {Number} - The absolute position of the current item. 35043 */ 35044 Owl.prototype.current = function(position) { 35045 if (position === undefined) { 35046 return this._current; 35047 } 35048 35049 if (this._items.length === 0) { 35050 return undefined; 35051 } 35052 35053 position = this.normalize(position); 35054 35055 if (this._current !== position) { 35056 var event = this.trigger('change', { property: { name: 'position', value: position } }); 35057 35058 if (event.data !== undefined) { 35059 position = this.normalize(event.data); 35060 } 35061 35062 this._current = position; 35063 35064 this.invalidate('position'); 35065 35066 this.trigger('changed', { property: { name: 'position', value: this._current } }); 35067 } 35068 35069 return this._current; 35070 }; 35071 35072 /** 35073 * Invalidates the given part of the update routine. 35074 * @param {String} [part] - The part to invalidate. 35075 * @returns {Array.<String>} - The invalidated parts. 35076 */ 35077 Owl.prototype.invalidate = function(part) { 35078 if ($.type(part) === 'string') { 35079 this._invalidated[part] = true; 35080 this.is('valid') && this.leave('valid'); 35081 } 35082 return $.map(this._invalidated, function(v, i) { return i }); 35083 }; 35084 35085 /** 35086 * Resets the absolute position of the current item. 35087 * @public 35088 * @param {Number} position - The absolute position of the new item. 35089 */ 35090 Owl.prototype.reset = function(position) { 35091 position = this.normalize(position); 35092 35093 if (position === undefined) { 35094 return; 35095 } 35096 35097 this._speed = 0; 35098 this._current = position; 35099 35100 this.suppress([ 'translate', 'translated' ]); 35101 35102 this.animate(this.coordinates(position)); 35103 35104 this.release([ 'translate', 'translated' ]); 35105 }; 35106 35107 /** 35108 * Normalizes an absolute or a relative position of an item. 35109 * @public 35110 * @param {Number} position - The absolute or relative position to normalize. 35111 * @param {Boolean} [relative=false] - Whether the given position is relative or not. 35112 * @returns {Number} - The normalized position. 35113 */ 35114 Owl.prototype.normalize = function(position, relative) { 35115 var n = this._items.length, 35116 m = relative ? 0 : this._clones.length; 35117 35118 if (!this.isNumeric(position) || n < 1) { 35119 position = undefined; 35120 } else if (position < 0 || position >= n + m) { 35121 position = ((position - m / 2) % n + n) % n + m / 2; 35122 } 35123 35124 return position; 35125 }; 35126 35127 /** 35128 * Converts an absolute position of an item into a relative one. 35129 * @public 35130 * @param {Number} position - The absolute position to convert. 35131 * @returns {Number} - The converted position. 35132 */ 35133 Owl.prototype.relative = function(position) { 35134 position -= this._clones.length / 2; 35135 return this.normalize(position, true); 35136 }; 35137 35138 /** 35139 * Gets the maximum position for the current item. 35140 * @public 35141 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position. 35142 * @returns {Number} 35143 */ 35144 Owl.prototype.maximum = function(relative) { 35145 var settings = this.settings, 35146 maximum = this._coordinates.length, 35147 iterator, 35148 reciprocalItemsWidth, 35149 elementWidth; 35150 35151 if (settings.loop) { 35152 maximum = this._clones.length / 2 + this._items.length - 1; 35153 } else if (settings.autoWidth || settings.merge) { 35154 iterator = this._items.length; 35155 reciprocalItemsWidth = this._items[--iterator].width(); 35156 elementWidth = this.$element.width(); 35157 while (iterator--) { 35158 reciprocalItemsWidth += this._items[iterator].width() + this.settings.margin; 35159 if (reciprocalItemsWidth > elementWidth) { 35160 break; 35161 } 35162 } 35163 maximum = iterator + 1; 35164 } else if (settings.center) { 35165 maximum = this._items.length - 1; 35166 } else { 35167 maximum = this._items.length - settings.items; 35168 } 35169 35170 if (relative) { 35171 maximum -= this._clones.length / 2; 35172 } 35173 35174 return Math.max(maximum, 0); 35175 }; 35176 35177 /** 35178 * Gets the minimum position for the current item. 35179 * @public 35180 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position. 35181 * @returns {Number} 35182 */ 35183 Owl.prototype.minimum = function(relative) { 35184 return relative ? 0 : this._clones.length / 2; 35185 }; 35186 35187 /** 35188 * Gets an item at the specified relative position. 35189 * @public 35190 * @param {Number} [position] - The relative position of the item. 35191 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given. 35192 */ 35193 Owl.prototype.items = function(position) { 35194 if (position === undefined) { 35195 return this._items.slice(); 35196 } 35197 35198 position = this.normalize(position, true); 35199 return this._items[position]; 35200 }; 35201 35202 /** 35203 * Gets an item at the specified relative position. 35204 * @public 35205 * @param {Number} [position] - The relative position of the item. 35206 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given. 35207 */ 35208 Owl.prototype.mergers = function(position) { 35209 if (position === undefined) { 35210 return this._mergers.slice(); 35211 } 35212 35213 position = this.normalize(position, true); 35214 return this._mergers[position]; 35215 }; 35216 35217 /** 35218 * Gets the absolute positions of clones for an item. 35219 * @public 35220 * @param {Number} [position] - The relative position of the item. 35221 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given. 35222 */ 35223 Owl.prototype.clones = function(position) { 35224 var odd = this._clones.length / 2, 35225 even = odd + this._items.length, 35226 map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 }; 35227 35228 if (position === undefined) { 35229 return $.map(this._clones, function(v, i) { return map(i) }); 35230 } 35231 35232 return $.map(this._clones, function(v, i) { return v === position ? map(i) : null }); 35233 }; 35234 35235 /** 35236 * Sets the current animation speed. 35237 * @public 35238 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged. 35239 * @returns {Number} - The current animation speed in milliseconds. 35240 */ 35241 Owl.prototype.speed = function(speed) { 35242 if (speed !== undefined) { 35243 this._speed = speed; 35244 } 35245 35246 return this._speed; 35247 }; 35248 35249 /** 35250 * Gets the coordinate of an item. 35251 * @todo The name of this method is missleanding. 35252 * @public 35253 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`. 35254 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates. 35255 */ 35256 Owl.prototype.coordinates = function(position) { 35257 var multiplier = 1, 35258 newPosition = position - 1, 35259 coordinate; 35260 35261 if (position === undefined) { 35262 return $.map(this._coordinates, $.proxy(function(coordinate, index) { 35263 return this.coordinates(index); 35264 }, this)); 35265 } 35266 35267 if (this.settings.center) { 35268 if (this.settings.rtl) { 35269 multiplier = -1; 35270 newPosition = position + 1; 35271 } 35272 35273 coordinate = this._coordinates[position]; 35274 coordinate += (this.width() - coordinate + (this._coordinates[newPosition] || 0)) / 2 * multiplier; 35275 } else { 35276 coordinate = this._coordinates[newPosition] || 0; 35277 } 35278 35279 coordinate = Math.ceil(coordinate); 35280 35281 return coordinate; 35282 }; 35283 35284 /** 35285 * Calculates the speed for a translation. 35286 * @protected 35287 * @param {Number} from - The absolute position of the start item. 35288 * @param {Number} to - The absolute position of the target item. 35289 * @param {Number} [factor=undefined] - The time factor in milliseconds. 35290 * @returns {Number} - The time in milliseconds for the translation. 35291 */ 35292 Owl.prototype.duration = function(from, to, factor) { 35293 if (factor === 0) { 35294 return 0; 35295 } 35296 35297 return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed)); 35298 }; 35299 35300 /** 35301 * Slides to the specified item. 35302 * @public 35303 * @param {Number} position - The position of the item. 35304 * @param {Number} [speed] - The time in milliseconds for the transition. 35305 */ 35306 Owl.prototype.to = function(position, speed) { 35307 var current = this.current(), 35308 revert = null, 35309 distance = position - this.relative(current), 35310 direction = (distance > 0) - (distance < 0), 35311 items = this._items.length, 35312 minimum = this.minimum(), 35313 maximum = this.maximum(); 35314 35315 if (this.settings.loop) { 35316 if (!this.settings.rewind && Math.abs(distance) > items / 2) { 35317 distance += direction * -1 * items; 35318 } 35319 35320 position = current + distance; 35321 revert = ((position - minimum) % items + items) % items + minimum; 35322 35323 if (revert !== position && revert - distance <= maximum && revert - distance > 0) { 35324 current = revert - distance; 35325 position = revert; 35326 this.reset(current); 35327 } 35328 } else if (this.settings.rewind) { 35329 maximum += 1; 35330 position = (position % maximum + maximum) % maximum; 35331 } else { 35332 position = Math.max(minimum, Math.min(maximum, position)); 35333 } 35334 35335 this.speed(this.duration(current, position, speed)); 35336 this.current(position); 35337 35338 if (this.$element.is(':visible')) { 35339 this.update(); 35340 } 35341 }; 35342 35343 /** 35344 * Slides to the next item. 35345 * @public 35346 * @param {Number} [speed] - The time in milliseconds for the transition. 35347 */ 35348 Owl.prototype.next = function(speed) { 35349 speed = speed || false; 35350 this.to(this.relative(this.current()) + 1, speed); 35351 }; 35352 35353 /** 35354 * Slides to the previous item. 35355 * @public 35356 * @param {Number} [speed] - The time in milliseconds for the transition. 35357 */ 35358 Owl.prototype.prev = function(speed) { 35359 speed = speed || false; 35360 this.to(this.relative(this.current()) - 1, speed); 35361 }; 35362 35363 /** 35364 * Handles the end of an animation. 35365 * @protected 35366 * @param {Event} event - The event arguments. 35367 */ 35368 Owl.prototype.onTransitionEnd = function(event) { 35369 35370 // if css2 animation then event object is undefined 35371 if (event !== undefined) { 35372 event.stopPropagation(); 35373 35374 // Catch only owl-stage transitionEnd event 35375 if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) { 35376 return false; 35377 } 35378 } 35379 35380 this.leave('animating'); 35381 this.trigger('translated'); 35382 }; 35383 35384 /** 35385 * Gets viewport width. 35386 * @protected 35387 * @return {Number} - The width in pixel. 35388 */ 35389 Owl.prototype.viewport = function() { 35390 var width; 35391 if (this.options.responsiveBaseElement !== window) { 35392 width = $(this.options.responsiveBaseElement).width(); 35393 } else if (window.innerWidth) { 35394 width = window.innerWidth; 35395 } else if (document.documentElement && document.documentElement.clientWidth) { 35396 width = document.documentElement.clientWidth; 35397 } else { 35398 console.warn('Can not detect viewport width.'); 35399 } 35400 return width; 35401 }; 35402 35403 /** 35404 * Replaces the current content. 35405 * @public 35406 * @param {HTMLElement|jQuery|String} content - The new content. 35407 */ 35408 Owl.prototype.replace = function(content) { 35409 this.$stage.empty(); 35410 this._items = []; 35411 35412 if (content) { 35413 content = (content instanceof jQuery) ? content : $(content); 35414 } 35415 35416 if (this.settings.nestedItemSelector) { 35417 content = content.find('.' + this.settings.nestedItemSelector); 35418 } 35419 35420 content.filter(function() { 35421 return this.nodeType === 1; 35422 }).each($.proxy(function(index, item) { 35423 item = this.prepare(item); 35424 this.$stage.append(item); 35425 this._items.push(item); 35426 this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1); 35427 }, this)); 35428 35429 this.reset(this.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0); 35430 35431 this.invalidate('items'); 35432 }; 35433 35434 /** 35435 * Adds an item. 35436 * @todo Use `item` instead of `content` for the event arguments. 35437 * @public 35438 * @param {HTMLElement|jQuery|String} content - The item content to add. 35439 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end. 35440 */ 35441 Owl.prototype.add = function(content, position) { 35442 var current = this.relative(this._current); 35443 35444 position = position === undefined ? this._items.length : this.normalize(position, true); 35445 content = content instanceof jQuery ? content : $(content); 35446 35447 this.trigger('add', { content: content, position: position }); 35448 35449 content = this.prepare(content); 35450 35451 if (this._items.length === 0 || position === this._items.length) { 35452 this._items.length === 0 && this.$stage.append(content); 35453 this._items.length !== 0 && this._items[position - 1].after(content); 35454 this._items.push(content); 35455 this._mergers.push(content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1); 35456 } else { 35457 this._items[position].before(content); 35458 this._items.splice(position, 0, content); 35459 this._mergers.splice(position, 0, content.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1); 35460 } 35461 35462 this._items[current] && this.reset(this._items[current].index()); 35463 35464 this.invalidate('items'); 35465 35466 this.trigger('added', { content: content, position: position }); 35467 }; 35468 35469 /** 35470 * Removes an item by its position. 35471 * @todo Use `item` instead of `content` for the event arguments. 35472 * @public 35473 * @param {Number} position - The relative position of the item to remove. 35474 */ 35475 Owl.prototype.remove = function(position) { 35476 position = this.normalize(position, true); 35477 35478 if (position === undefined) { 35479 return; 35480 } 35481 35482 this.trigger('remove', { content: this._items[position], position: position }); 35483 35484 this._items[position].remove(); 35485 this._items.splice(position, 1); 35486 this._mergers.splice(position, 1); 35487 35488 this.invalidate('items'); 35489 35490 this.trigger('removed', { content: null, position: position }); 35491 }; 35492 35493 /** 35494 * Preloads images with auto width. 35495 * @todo Replace by a more generic approach 35496 * @protected 35497 */ 35498 Owl.prototype.preloadAutoWidthImages = function(images) { 35499 images.each($.proxy(function(i, element) { 35500 this.enter('pre-loading'); 35501 element = $(element); 35502 $(new Image()).one('load', $.proxy(function(e) { 35503 element.attr('src', e.target.src); 35504 element.css('opacity', 1); 35505 this.leave('pre-loading'); 35506 !this.is('pre-loading') && !this.is('initializing') && this.refresh(); 35507 }, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina')); 35508 }, this)); 35509 }; 35510 35511 /** 35512 * Destroys the carousel. 35513 * @public 35514 */ 35515 Owl.prototype.destroy = function() { 35516 35517 this.$element.off('.owl.core'); 35518 this.$stage.off('.owl.core'); 35519 $(document).off('.owl.core'); 35520 35521 if (this.settings.responsive !== false) { 35522 window.clearTimeout(this.resizeTimer); 35523 this.off(window, 'resize', this._handlers.onThrottledResize); 35524 } 35525 35526 for (var i in this._plugins) { 35527 this._plugins[i].destroy(); 35528 } 35529 35530 this.$stage.children('.cloned').remove(); 35531 35532 this.$stage.unwrap(); 35533 this.$stage.children().contents().unwrap(); 35534 this.$stage.children().unwrap(); 35535 35536 this.$element 35537 .removeClass(this.options.refreshClass) 35538 .removeClass(this.options.loadingClass) 35539 .removeClass(this.options.loadedClass) 35540 .removeClass(this.options.rtlClass) 35541 .removeClass(this.options.dragClass) 35542 .removeClass(this.options.grabClass) 35543 .attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), '')) 35544 .removeData('owl.carousel'); 35545 }; 35546 35547 /** 35548 * Operators to calculate right-to-left and left-to-right. 35549 * @protected 35550 * @param {Number} [a] - The left side operand. 35551 * @param {String} [o] - The operator. 35552 * @param {Number} [b] - The right side operand. 35553 */ 35554 Owl.prototype.op = function(a, o, b) { 35555 var rtl = this.settings.rtl; 35556 switch (o) { 35557 case '<': 35558 return rtl ? a > b : a < b; 35559 case '>': 35560 return rtl ? a < b : a > b; 35561 case '>=': 35562 return rtl ? a <= b : a >= b; 35563 case '<=': 35564 return rtl ? a >= b : a <= b; 35565 default: 35566 break; 35567 } 35568 }; 35569 35570 /** 35571 * Attaches to an internal event. 35572 * @protected 35573 * @param {HTMLElement} element - The event source. 35574 * @param {String} event - The event name. 35575 * @param {Function} listener - The event handler to attach. 35576 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not. 35577 */ 35578 Owl.prototype.on = function(element, event, listener, capture) { 35579 if (element.addEventListener) { 35580 element.addEventListener(event, listener, capture); 35581 } else if (element.attachEvent) { 35582 element.attachEvent('on' + event, listener); 35583 } 35584 }; 35585 35586 /** 35587 * Detaches from an internal event. 35588 * @protected 35589 * @param {HTMLElement} element - The event source. 35590 * @param {String} event - The event name. 35591 * @param {Function} listener - The attached event handler to detach. 35592 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not. 35593 */ 35594 Owl.prototype.off = function(element, event, listener, capture) { 35595 if (element.removeEventListener) { 35596 element.removeEventListener(event, listener, capture); 35597 } else if (element.detachEvent) { 35598 element.detachEvent('on' + event, listener); 35599 } 35600 }; 35601 35602 /** 35603 * Triggers a public event. 35604 * @todo Remove `status`, `relatedTarget` should be used instead. 35605 * @protected 35606 * @param {String} name - The event name. 35607 * @param {*} [data=null] - The event data. 35608 * @param {String} [namespace=carousel] - The event namespace. 35609 * @param {String} [state] - The state which is associated with the event. 35610 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not. 35611 * @returns {Event} - The event arguments. 35612 */ 35613 Owl.prototype.trigger = function(name, data, namespace, state, enter) { 35614 var status = { 35615 item: { count: this._items.length, index: this.current() } 35616 }, handler = $.camelCase( 35617 $.grep([ 'on', name, namespace ], function(v) { return v }) 35618 .join('-').toLowerCase() 35619 ), event = $.Event( 35620 [ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(), 35621 $.extend({ relatedTarget: this }, status, data) 35622 ); 35623 35624 if (!this._supress[name]) { 35625 $.each(this._plugins, function(name, plugin) { 35626 if (plugin.onTrigger) { 35627 plugin.onTrigger(event); 35628 } 35629 }); 35630 35631 this.register({ type: Owl.Type.Event, name: name }); 35632 this.$element.trigger(event); 35633 35634 if (this.settings && typeof this.settings[handler] === 'function') { 35635 this.settings[handler].call(this, event); 35636 } 35637 } 35638 35639 return event; 35640 }; 35641 35642 /** 35643 * Enters a state. 35644 * @param name - The state name. 35645 */ 35646 Owl.prototype.enter = function(name) { 35647 $.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) { 35648 if (this._states.current[name] === undefined) { 35649 this._states.current[name] = 0; 35650 } 35651 35652 this._states.current[name]++; 35653 }, this)); 35654 }; 35655 35656 /** 35657 * Leaves a state. 35658 * @param name - The state name. 35659 */ 35660 Owl.prototype.leave = function(name) { 35661 $.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) { 35662 this._states.current[name]--; 35663 }, this)); 35664 }; 35665 35666 /** 35667 * Registers an event or state. 35668 * @public 35669 * @param {Object} object - The event or state to register. 35670 */ 35671 Owl.prototype.register = function(object) { 35672 if (object.type === Owl.Type.Event) { 35673 if (!$.event.special[object.name]) { 35674 $.event.special[object.name] = {}; 35675 } 35676 35677 if (!$.event.special[object.name].owl) { 35678 var _default = $.event.special[object.name]._default; 35679 $.event.special[object.name]._default = function(e) { 35680 if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) { 35681 return _default.apply(this, arguments); 35682 } 35683 return e.namespace && e.namespace.indexOf('owl') > -1; 35684 }; 35685 $.event.special[object.name].owl = true; 35686 } 35687 } else if (object.type === Owl.Type.State) { 35688 if (!this._states.tags[object.name]) { 35689 this._states.tags[object.name] = object.tags; 35690 } else { 35691 this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags); 35692 } 35693 35694 this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) { 35695 return $.inArray(tag, this._states.tags[object.name]) === i; 35696 }, this)); 35697 } 35698 }; 35699 35700 /** 35701 * Suppresses events. 35702 * @protected 35703 * @param {Array.<String>} events - The events to suppress. 35704 */ 35705 Owl.prototype.suppress = function(events) { 35706 $.each(events, $.proxy(function(index, event) { 35707 this._supress[event] = true; 35708 }, this)); 35709 }; 35710 35711 /** 35712 * Releases suppressed events. 35713 * @protected 35714 * @param {Array.<String>} events - The events to release. 35715 */ 35716 Owl.prototype.release = function(events) { 35717 $.each(events, $.proxy(function(index, event) { 35718 delete this._supress[event]; 35719 }, this)); 35720 }; 35721 35722 /** 35723 * Gets unified pointer coordinates from event. 35724 * @todo #261 35725 * @protected 35726 * @param {Event} - The `mousedown` or `touchstart` event. 35727 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position. 35728 */ 35729 Owl.prototype.pointer = function(event) { 35730 var result = { x: null, y: null }; 35731 35732 event = event.originalEvent || event || window.event; 35733 35734 event = event.touches && event.touches.length ? 35735 event.touches[0] : event.changedTouches && event.changedTouches.length ? 35736 event.changedTouches[0] : event; 35737 35738 if (event.pageX) { 35739 result.x = event.pageX; 35740 result.y = event.pageY; 35741 } else { 35742 result.x = event.clientX; 35743 result.y = event.clientY; 35744 } 35745 35746 return result; 35747 }; 35748 35749 /** 35750 * Determines if the input is a Number or something that can be coerced to a Number 35751 * @protected 35752 * @param {Number|String|Object|Array|Boolean|RegExp|Function|Symbol} - The input to be tested 35753 * @returns {Boolean} - An indication if the input is a Number or can be coerced to a Number 35754 */ 35755 Owl.prototype.isNumeric = function(number) { 35756 return !isNaN(parseFloat(number)); 35757 }; 35758 35759 /** 35760 * Gets the difference of two vectors. 35761 * @todo #261 35762 * @protected 35763 * @param {Object} - The first vector. 35764 * @param {Object} - The second vector. 35765 * @returns {Object} - The difference. 35766 */ 35767 Owl.prototype.difference = function(first, second) { 35768 return { 35769 x: first.x - second.x, 35770 y: first.y - second.y 35771 }; 35772 }; 35773 35774 /** 35775 * The jQuery Plugin for the Owl Carousel 35776 * @todo Navigation plugin `next` and `prev` 35777 * @public 35778 */ 35779 $.fn.owlCarousel = function(option) { 35780 var args = Array.prototype.slice.call(arguments, 1); 35781 35782 return this.each(function() { 35783 var $this = $(this), 35784 data = $this.data('owl.carousel'); 35785 35786 if (!data) { 35787 data = new Owl(this, typeof option == 'object' && option); 35788 $this.data('owl.carousel', data); 35789 35790 $.each([ 35791 'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove' 35792 ], function(i, event) { 35793 data.register({ type: Owl.Type.Event, name: event }); 35794 data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) { 35795 if (e.namespace && e.relatedTarget !== this) { 35796 this.suppress([ event ]); 35797 data[event].apply(this, [].slice.call(arguments, 1)); 35798 this.release([ event ]); 35799 } 35800 }, data)); 35801 }); 35802 } 35803 35804 if (typeof option == 'string' && option.charAt(0) !== '_') { 35805 data[option].apply(data, args); 35806 } 35807 }); 35808 }; 35809 35810 /** 35811 * The constructor for the jQuery Plugin 35812 * @public 35813 */ 35814 $.fn.owlCarousel.Constructor = Owl; 35815 35816})(window.Zepto || window.jQuery, window, document); 35817 35818/** 35819 * AutoRefresh Plugin 35820 * @version 2.1.0 35821 * @author Artus Kolanowski 35822 * @author David Deutsch 35823 * @license The MIT License (MIT) 35824 */ 35825;(function($, window, document, undefined) { 35826 35827 /** 35828 * Creates the auto refresh plugin. 35829 * @class The Auto Refresh Plugin 35830 * @param {Owl} carousel - The Owl Carousel 35831 */ 35832 var AutoRefresh = function(carousel) { 35833 /** 35834 * Reference to the core. 35835 * @protected 35836 * @type {Owl} 35837 */ 35838 this._core = carousel; 35839 35840 /** 35841 * Refresh interval. 35842 * @protected 35843 * @type {number} 35844 */ 35845 this._interval = null; 35846 35847 /** 35848 * Whether the element is currently visible or not. 35849 * @protected 35850 * @type {Boolean} 35851 */ 35852 this._visible = null; 35853 35854 /** 35855 * All event handlers. 35856 * @protected 35857 * @type {Object} 35858 */ 35859 this._handlers = { 35860 'initialized.owl.carousel': $.proxy(function(e) { 35861 if (e.namespace && this._core.settings.autoRefresh) { 35862 this.watch(); 35863 } 35864 }, this) 35865 }; 35866 35867 // set default options 35868 this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options); 35869 35870 // register event handlers 35871 this._core.$element.on(this._handlers); 35872 }; 35873 35874 /** 35875 * Default options. 35876 * @public 35877 */ 35878 AutoRefresh.Defaults = { 35879 autoRefresh: true, 35880 autoRefreshInterval: 500 35881 }; 35882 35883 /** 35884 * Watches the element. 35885 */ 35886 AutoRefresh.prototype.watch = function() { 35887 if (this._interval) { 35888 return; 35889 } 35890 35891 this._visible = this._core.$element.is(':visible'); 35892 this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval); 35893 }; 35894 35895 /** 35896 * Refreshes the element. 35897 */ 35898 AutoRefresh.prototype.refresh = function() { 35899 if (this._core.$element.is(':visible') === this._visible) { 35900 return; 35901 } 35902 35903 this._visible = !this._visible; 35904 35905 this._core.$element.toggleClass('owl-hidden', !this._visible); 35906 35907 this._visible && (this._core.invalidate('width') && this._core.refresh()); 35908 }; 35909 35910 /** 35911 * Destroys the plugin. 35912 */ 35913 AutoRefresh.prototype.destroy = function() { 35914 var handler, property; 35915 35916 window.clearInterval(this._interval); 35917 35918 for (handler in this._handlers) { 35919 this._core.$element.off(handler, this._handlers[handler]); 35920 } 35921 for (property in Object.getOwnPropertyNames(this)) { 35922 typeof this[property] != 'function' && (this[property] = null); 35923 } 35924 }; 35925 35926 $.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh; 35927 35928})(window.Zepto || window.jQuery, window, document); 35929 35930/** 35931 * Lazy Plugin 35932 * @version 2.1.0 35933 * @author Bartosz Wojciechowski 35934 * @author David Deutsch 35935 * @license The MIT License (MIT) 35936 */ 35937;(function($, window, document, undefined) { 35938 35939 /** 35940 * Creates the lazy plugin. 35941 * @class The Lazy Plugin 35942 * @param {Owl} carousel - The Owl Carousel 35943 */ 35944 var Lazy = function(carousel) { 35945 35946 /** 35947 * Reference to the core. 35948 * @protected 35949 * @type {Owl} 35950 */ 35951 this._core = carousel; 35952 35953 /** 35954 * Already loaded items. 35955 * @protected 35956 * @type {Array.<jQuery>} 35957 */ 35958 this._loaded = []; 35959 35960 /** 35961 * Event handlers. 35962 * @protected 35963 * @type {Object} 35964 */ 35965 this._handlers = { 35966 'initialized.owl.carousel change.owl.carousel resized.owl.carousel': $.proxy(function(e) { 35967 if (!e.namespace) { 35968 return; 35969 } 35970 35971 if (!this._core.settings || !this._core.settings.lazyLoad) { 35972 return; 35973 } 35974 35975 if ((e.property && e.property.name == 'position') || e.type == 'initialized') { 35976 var settings = this._core.settings, 35977 n = (settings.center && Math.ceil(settings.items / 2) || settings.items), 35978 i = ((settings.center && n * -1) || 0), 35979 position = (e.property && e.property.value !== undefined ? e.property.value : this._core.current()) + i, 35980 clones = this._core.clones().length, 35981 load = $.proxy(function(i, v) { this.load(v) }, this); 35982 35983 while (i++ < n) { 35984 this.load(clones / 2 + this._core.relative(position)); 35985 clones && $.each(this._core.clones(this._core.relative(position)), load); 35986 position++; 35987 } 35988 } 35989 }, this) 35990 }; 35991 35992 // set the default options 35993 this._core.options = $.extend({}, Lazy.Defaults, this._core.options); 35994 35995 // register event handler 35996 this._core.$element.on(this._handlers); 35997 }; 35998 35999 /** 36000 * Default options. 36001 * @public 36002 */ 36003 Lazy.Defaults = { 36004 lazyLoad: false 36005 }; 36006 36007 /** 36008 * Loads all resources of an item at the specified position. 36009 * @param {Number} position - The absolute position of the item. 36010 * @protected 36011 */ 36012 Lazy.prototype.load = function(position) { 36013 var $item = this._core.$stage.children().eq(position), 36014 $elements = $item && $item.find('.owl-lazy'); 36015 36016 if (!$elements || $.inArray($item.get(0), this._loaded) > -1) { 36017 return; 36018 } 36019 36020 $elements.each($.proxy(function(index, element) { 36021 var $element = $(element), image, 36022 url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src'); 36023 36024 this._core.trigger('load', { element: $element, url: url }, 'lazy'); 36025 36026 if ($element.is('img')) { 36027 $element.one('load.owl.lazy', $.proxy(function() { 36028 $element.css('opacity', 1); 36029 this._core.trigger('loaded', { element: $element, url: url }, 'lazy'); 36030 }, this)).attr('src', url); 36031 } else { 36032 image = new Image(); 36033 image.onload = $.proxy(function() { 36034 $element.css({ 36035 'background-image': 'url("' + url + '")', 36036 'opacity': '1' 36037 }); 36038 this._core.trigger('loaded', { element: $element, url: url }, 'lazy'); 36039 }, this); 36040 image.src = url; 36041 } 36042 }, this)); 36043 36044 this._loaded.push($item.get(0)); 36045 }; 36046 36047 /** 36048 * Destroys the plugin. 36049 * @public 36050 */ 36051 Lazy.prototype.destroy = function() { 36052 var handler, property; 36053 36054 for (handler in this.handlers) { 36055 this._core.$element.off(handler, this.handlers[handler]); 36056 } 36057 for (property in Object.getOwnPropertyNames(this)) { 36058 typeof this[property] != 'function' && (this[property] = null); 36059 } 36060 }; 36061 36062 $.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy; 36063 36064})(window.Zepto || window.jQuery, window, document); 36065 36066/** 36067 * AutoHeight Plugin 36068 * @version 2.1.0 36069 * @author Bartosz Wojciechowski 36070 * @author David Deutsch 36071 * @license The MIT License (MIT) 36072 */ 36073;(function($, window, document, undefined) { 36074 36075 /** 36076 * Creates the auto height plugin. 36077 * @class The Auto Height Plugin 36078 * @param {Owl} carousel - The Owl Carousel 36079 */ 36080 var AutoHeight = function(carousel) { 36081 /** 36082 * Reference to the core. 36083 * @protected 36084 * @type {Owl} 36085 */ 36086 this._core = carousel; 36087 36088 /** 36089 * All event handlers. 36090 * @protected 36091 * @type {Object} 36092 */ 36093 this._handlers = { 36094 'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) { 36095 if (e.namespace && this._core.settings.autoHeight) { 36096 this.update(); 36097 } 36098 }, this), 36099 'changed.owl.carousel': $.proxy(function(e) { 36100 if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){ 36101 this.update(); 36102 } 36103 }, this), 36104 'loaded.owl.lazy': $.proxy(function(e) { 36105 if (e.namespace && this._core.settings.autoHeight 36106 && e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) { 36107 this.update(); 36108 } 36109 }, this) 36110 }; 36111 36112 // set default options 36113 this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options); 36114 36115 // register event handlers 36116 this._core.$element.on(this._handlers); 36117 }; 36118 36119 /** 36120 * Default options. 36121 * @public 36122 */ 36123 AutoHeight.Defaults = { 36124 autoHeight: false, 36125 autoHeightClass: 'owl-height' 36126 }; 36127 36128 /** 36129 * Updates the view. 36130 */ 36131 AutoHeight.prototype.update = function() { 36132 var start = this._core._current, 36133 end = start + this._core.settings.items, 36134 visible = this._core.$stage.children().toArray().slice(start, end), 36135 heights = [], 36136 maxheight = 0; 36137 36138 $.each(visible, function(index, item) { 36139 heights.push($(item).height()); 36140 }); 36141 36142 maxheight = Math.max.apply(null, heights); 36143 36144 this._core.$stage.parent() 36145 .height(maxheight) 36146 .addClass(this._core.settings.autoHeightClass); 36147 }; 36148 36149 AutoHeight.prototype.destroy = function() { 36150 var handler, property; 36151 36152 for (handler in this._handlers) { 36153 this._core.$element.off(handler, this._handlers[handler]); 36154 } 36155 for (property in Object.getOwnPropertyNames(this)) { 36156 typeof this[property] != 'function' && (this[property] = null); 36157 } 36158 }; 36159 36160 $.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight; 36161 36162})(window.Zepto || window.jQuery, window, document); 36163 36164/** 36165 * Video Plugin 36166 * @version 2.1.0 36167 * @author Bartosz Wojciechowski 36168 * @author David Deutsch 36169 * @license The MIT License (MIT) 36170 */ 36171;(function($, window, document, undefined) { 36172 36173 /** 36174 * Creates the video plugin. 36175 * @class The Video Plugin 36176 * @param {Owl} carousel - The Owl Carousel 36177 */ 36178 var Video = function(carousel) { 36179 /** 36180 * Reference to the core. 36181 * @protected 36182 * @type {Owl} 36183 */ 36184 this._core = carousel; 36185 36186 /** 36187 * Cache all video URLs. 36188 * @protected 36189 * @type {Object} 36190 */ 36191 this._videos = {}; 36192 36193 /** 36194 * Current playing item. 36195 * @protected 36196 * @type {jQuery} 36197 */ 36198 this._playing = null; 36199 36200 /** 36201 * All event handlers. 36202 * @todo The cloned content removale is too late 36203 * @protected 36204 * @type {Object} 36205 */ 36206 this._handlers = { 36207 'initialized.owl.carousel': $.proxy(function(e) { 36208 if (e.namespace) { 36209 this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] }); 36210 } 36211 }, this), 36212 'resize.owl.carousel': $.proxy(function(e) { 36213 if (e.namespace && this._core.settings.video && this.isInFullScreen()) { 36214 e.preventDefault(); 36215 } 36216 }, this), 36217 'refreshed.owl.carousel': $.proxy(function(e) { 36218 if (e.namespace && this._core.is('resizing')) { 36219 this._core.$stage.find('.cloned .owl-video-frame').remove(); 36220 } 36221 }, this), 36222 'changed.owl.carousel': $.proxy(function(e) { 36223 if (e.namespace && e.property.name === 'position' && this._playing) { 36224 this.stop(); 36225 } 36226 }, this), 36227 'prepared.owl.carousel': $.proxy(function(e) { 36228 if (!e.namespace) { 36229 return; 36230 } 36231 36232 var $element = $(e.content).find('.owl-video'); 36233 36234 if ($element.length) { 36235 $element.css('display', 'none'); 36236 this.fetch($element, $(e.content)); 36237 } 36238 }, this) 36239 }; 36240 36241 // set default options 36242 this._core.options = $.extend({}, Video.Defaults, this._core.options); 36243 36244 // register event handlers 36245 this._core.$element.on(this._handlers); 36246 36247 this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) { 36248 this.play(e); 36249 }, this)); 36250 }; 36251 36252 /** 36253 * Default options. 36254 * @public 36255 */ 36256 Video.Defaults = { 36257 video: false, 36258 videoHeight: false, 36259 videoWidth: false 36260 }; 36261 36262 /** 36263 * Gets the video ID and the type (YouTube/Vimeo/vzaar only). 36264 * @protected 36265 * @param {jQuery} target - The target containing the video data. 36266 * @param {jQuery} item - The item containing the video. 36267 */ 36268 Video.prototype.fetch = function(target, item) { 36269 var type = (function() { 36270 if (target.attr('data-vimeo-id')) { 36271 return 'vimeo'; 36272 } else if (target.attr('data-vzaar-id')) { 36273 return 'vzaar' 36274 } else { 36275 return 'youtube'; 36276 } 36277 })(), 36278 id = target.attr('data-vimeo-id') || target.attr('data-youtube-id') || target.attr('data-vzaar-id'), 36279 width = target.attr('data-width') || this._core.settings.videoWidth, 36280 height = target.attr('data-height') || this._core.settings.videoHeight, 36281 url = target.attr('href'); 36282 36283 if (url) { 36284 36285 /* 36286 Parses the id's out of the following urls (and probably more): 36287 https://www.youtube.com/watch?v=:id 36288 https://youtu.be/:id 36289 https://vimeo.com/:id 36290 https://vimeo.com/channels/:channel/:id 36291 https://vimeo.com/groups/:group/videos/:id 36292 https://app.vzaar.com/videos/:id 36293 36294 Visual example: https://regexper.com/#(http%3A%7Chttps%3A%7C)%5C%2F%5C%2F(player.%7Cwww.%7Capp.)%3F(vimeo%5C.com%7Cyoutu(be%5C.com%7C%5C.be%7Cbe%5C.googleapis%5C.com)%7Cvzaar%5C.com)%5C%2F(video%5C%2F%7Cvideos%5C%2F%7Cembed%5C%2F%7Cchannels%5C%2F.%2B%5C%2F%7Cgroups%5C%2F.%2B%5C%2F%7Cwatch%5C%3Fv%3D%7Cv%5C%2F)%3F(%5BA-Za-z0-9._%25-%5D*)(%5C%26%5CS%2B)%3F 36295 */ 36296 36297 id = url.match(/(http:|https:|)\/\/(player.|www.|app.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com)|vzaar\.com)\/(video\/|videos\/|embed\/|channels\/.+\/|groups\/.+\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/); 36298 36299 if (id[3].indexOf('youtu') > -1) { 36300 type = 'youtube'; 36301 } else if (id[3].indexOf('vimeo') > -1) { 36302 type = 'vimeo'; 36303 } else if (id[3].indexOf('vzaar') > -1) { 36304 type = 'vzaar'; 36305 } else { 36306 throw new Error('Video URL not supported.'); 36307 } 36308 id = id[6]; 36309 } else { 36310 throw new Error('Missing video URL.'); 36311 } 36312 36313 this._videos[url] = { 36314 type: type, 36315 id: id, 36316 width: width, 36317 height: height 36318 }; 36319 36320 item.attr('data-video', url); 36321 36322 this.thumbnail(target, this._videos[url]); 36323 }; 36324 36325 /** 36326 * Creates video thumbnail. 36327 * @protected 36328 * @param {jQuery} target - The target containing the video data. 36329 * @param {Object} info - The video info object. 36330 * @see `fetch` 36331 */ 36332 Video.prototype.thumbnail = function(target, video) { 36333 var tnLink, 36334 icon, 36335 path, 36336 dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '', 36337 customTn = target.find('img'), 36338 srcType = 'src', 36339 lazyClass = '', 36340 settings = this._core.settings, 36341 create = function(path) { 36342 icon = '<div class="owl-video-play-icon"></div>'; 36343 36344 if (settings.lazyLoad) { 36345 tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>'; 36346 } else { 36347 tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>'; 36348 } 36349 target.after(tnLink); 36350 target.after(icon); 36351 }; 36352 36353 // wrap video content into owl-video-wrapper div 36354 target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>'); 36355 36356 if (this._core.settings.lazyLoad) { 36357 srcType = 'data-src'; 36358 lazyClass = 'owl-lazy'; 36359 } 36360 36361 // custom thumbnail 36362 if (customTn.length) { 36363 create(customTn.attr(srcType)); 36364 customTn.remove(); 36365 return false; 36366 } 36367 36368 if (video.type === 'youtube') { 36369 path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg"; 36370 create(path); 36371 } else if (video.type === 'vimeo') { 36372 $.ajax({ 36373 type: 'GET', 36374 url: '//vimeo.com/api/v2/video/' + video.id + '.json', 36375 jsonp: 'callback', 36376 dataType: 'jsonp', 36377 success: function(data) { 36378 path = data[0].thumbnail_large; 36379 create(path); 36380 } 36381 }); 36382 } else if (video.type === 'vzaar') { 36383 $.ajax({ 36384 type: 'GET', 36385 url: '//vzaar.com/api/videos/' + video.id + '.json', 36386 jsonp: 'callback', 36387 dataType: 'jsonp', 36388 success: function(data) { 36389 path = data.framegrab_url; 36390 create(path); 36391 } 36392 }); 36393 } 36394 }; 36395 36396 /** 36397 * Stops the current video. 36398 * @public 36399 */ 36400 Video.prototype.stop = function() { 36401 this._core.trigger('stop', null, 'video'); 36402 this._playing.find('.owl-video-frame').remove(); 36403 this._playing.removeClass('owl-video-playing'); 36404 this._playing = null; 36405 this._core.leave('playing'); 36406 this._core.trigger('stopped', null, 'video'); 36407 }; 36408 36409 /** 36410 * Starts the current video. 36411 * @public 36412 * @param {Event} event - The event arguments. 36413 */ 36414 Video.prototype.play = function(event) { 36415 var target = $(event.target), 36416 item = target.closest('.' + this._core.settings.itemClass), 36417 video = this._videos[item.attr('data-video')], 36418 width = video.width || '100%', 36419 height = video.height || this._core.$stage.height(), 36420 html; 36421 36422 if (this._playing) { 36423 return; 36424 } 36425 36426 this._core.enter('playing'); 36427 this._core.trigger('play', null, 'video'); 36428 36429 item = this._core.items(this._core.relative(item.index())); 36430 36431 this._core.reset(item.index()); 36432 36433 if (video.type === 'youtube') { 36434 html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' + 36435 video.id + '?autoplay=1&rel=0&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>'; 36436 } else if (video.type === 'vimeo') { 36437 html = '<iframe src="//player.vimeo.com/video/' + video.id + 36438 '?autoplay=1" width="' + width + '" height="' + height + 36439 '" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'; 36440 } else if (video.type === 'vzaar') { 36441 html = '<iframe frameborder="0"' + 'height="' + height + '"' + 'width="' + width + 36442 '" allowfullscreen mozallowfullscreen webkitAllowFullScreen ' + 36443 'src="//view.vzaar.com/' + video.id + '/player?autoplay=true"></iframe>'; 36444 } 36445 36446 $('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video')); 36447 36448 this._playing = item.addClass('owl-video-playing'); 36449 }; 36450 36451 /** 36452 * Checks whether an video is currently in full screen mode or not. 36453 * @todo Bad style because looks like a readonly method but changes members. 36454 * @protected 36455 * @returns {Boolean} 36456 */ 36457 Video.prototype.isInFullScreen = function() { 36458 var element = document.fullscreenElement || document.mozFullScreenElement || 36459 document.webkitFullscreenElement; 36460 36461 return element && $(element).parent().hasClass('owl-video-frame'); 36462 }; 36463 36464 /** 36465 * Destroys the plugin. 36466 */ 36467 Video.prototype.destroy = function() { 36468 var handler, property; 36469 36470 this._core.$element.off('click.owl.video'); 36471 36472 for (handler in this._handlers) { 36473 this._core.$element.off(handler, this._handlers[handler]); 36474 } 36475 for (property in Object.getOwnPropertyNames(this)) { 36476 typeof this[property] != 'function' && (this[property] = null); 36477 } 36478 }; 36479 36480 $.fn.owlCarousel.Constructor.Plugins.Video = Video; 36481 36482})(window.Zepto || window.jQuery, window, document); 36483 36484/** 36485 * Animate Plugin 36486 * @version 2.1.0 36487 * @author Bartosz Wojciechowski 36488 * @author David Deutsch 36489 * @license The MIT License (MIT) 36490 */ 36491;(function($, window, document, undefined) { 36492 36493 /** 36494 * Creates the animate plugin. 36495 * @class The Navigation Plugin 36496 * @param {Owl} scope - The Owl Carousel 36497 */ 36498 var Animate = function(scope) { 36499 this.core = scope; 36500 this.core.options = $.extend({}, Animate.Defaults, this.core.options); 36501 this.swapping = true; 36502 this.previous = undefined; 36503 this.next = undefined; 36504 36505 this.handlers = { 36506 'change.owl.carousel': $.proxy(function(e) { 36507 if (e.namespace && e.property.name == 'position') { 36508 this.previous = this.core.current(); 36509 this.next = e.property.value; 36510 } 36511 }, this), 36512 'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) { 36513 if (e.namespace) { 36514 this.swapping = e.type == 'translated'; 36515 } 36516 }, this), 36517 'translate.owl.carousel': $.proxy(function(e) { 36518 if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) { 36519 this.swap(); 36520 } 36521 }, this) 36522 }; 36523 36524 this.core.$element.on(this.handlers); 36525 }; 36526 36527 /** 36528 * Default options. 36529 * @public 36530 */ 36531 Animate.Defaults = { 36532 animateOut: false, 36533 animateIn: false 36534 }; 36535 36536 /** 36537 * Toggles the animation classes whenever an translations starts. 36538 * @protected 36539 * @returns {Boolean|undefined} 36540 */ 36541 Animate.prototype.swap = function() { 36542 36543 if (this.core.settings.items !== 1) { 36544 return; 36545 } 36546 36547 if (!$.support.animation || !$.support.transition) { 36548 return; 36549 } 36550 36551 this.core.speed(0); 36552 36553 var left, 36554 clear = $.proxy(this.clear, this), 36555 previous = this.core.$stage.children().eq(this.previous), 36556 next = this.core.$stage.children().eq(this.next), 36557 incoming = this.core.settings.animateIn, 36558 outgoing = this.core.settings.animateOut; 36559 36560 if (this.core.current() === this.previous) { 36561 return; 36562 } 36563 36564 if (outgoing) { 36565 left = this.core.coordinates(this.previous) - this.core.coordinates(this.next); 36566 previous.one($.support.animation.end, clear) 36567 .css( { 'left': left + 'px' } ) 36568 .addClass('animated owl-animated-out') 36569 .addClass(outgoing); 36570 } 36571 36572 if (incoming) { 36573 next.one($.support.animation.end, clear) 36574 .addClass('animated owl-animated-in') 36575 .addClass(incoming); 36576 } 36577 }; 36578 36579 Animate.prototype.clear = function(e) { 36580 $(e.target).css( { 'left': '' } ) 36581 .removeClass('animated owl-animated-out owl-animated-in') 36582 .removeClass(this.core.settings.animateIn) 36583 .removeClass(this.core.settings.animateOut); 36584 this.core.onTransitionEnd(); 36585 }; 36586 36587 /** 36588 * Destroys the plugin. 36589 * @public 36590 */ 36591 Animate.prototype.destroy = function() { 36592 var handler, property; 36593 36594 for (handler in this.handlers) { 36595 this.core.$element.off(handler, this.handlers[handler]); 36596 } 36597 for (property in Object.getOwnPropertyNames(this)) { 36598 typeof this[property] != 'function' && (this[property] = null); 36599 } 36600 }; 36601 36602 $.fn.owlCarousel.Constructor.Plugins.Animate = Animate; 36603 36604})(window.Zepto || window.jQuery, window, document); 36605 36606/** 36607 * Autoplay Plugin 36608 * @version 2.1.0 36609 * @author Bartosz Wojciechowski 36610 * @author Artus Kolanowski 36611 * @author David Deutsch 36612 * @license The MIT License (MIT) 36613 */ 36614;(function($, window, document, undefined) { 36615 36616 /** 36617 * Creates the autoplay plugin. 36618 * @class The Autoplay Plugin 36619 * @param {Owl} scope - The Owl Carousel 36620 */ 36621 var Autoplay = function(carousel) { 36622 /** 36623 * Reference to the core. 36624 * @protected 36625 * @type {Owl} 36626 */ 36627 this._core = carousel; 36628 36629 /** 36630 * The autoplay timeout. 36631 * @type {Timeout} 36632 */ 36633 this._timeout = null; 36634 36635 /** 36636 * Indicates whenever the autoplay is paused. 36637 * @type {Boolean} 36638 */ 36639 this._paused = false; 36640 36641 /** 36642 * All event handlers. 36643 * @protected 36644 * @type {Object} 36645 */ 36646 this._handlers = { 36647 'changed.owl.carousel': $.proxy(function(e) { 36648 if (e.namespace && e.property.name === 'settings') { 36649 if (this._core.settings.autoplay) { 36650 this.play(); 36651 } else { 36652 this.stop(); 36653 } 36654 } else if (e.namespace && e.property.name === 'position') { 36655 //console.log('play?', e); 36656 if (this._core.settings.autoplay) { 36657 this._setAutoPlayInterval(); 36658 } 36659 } 36660 }, this), 36661 'initialized.owl.carousel': $.proxy(function(e) { 36662 if (e.namespace && this._core.settings.autoplay) { 36663 this.play(); 36664 } 36665 }, this), 36666 'play.owl.autoplay': $.proxy(function(e, t, s) { 36667 if (e.namespace) { 36668 this.play(t, s); 36669 } 36670 }, this), 36671 'stop.owl.autoplay': $.proxy(function(e) { 36672 if (e.namespace) { 36673 this.stop(); 36674 } 36675 }, this), 36676 'mouseover.owl.autoplay': $.proxy(function() { 36677 if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) { 36678 this.pause(); 36679 } 36680 }, this), 36681 'mouseleave.owl.autoplay': $.proxy(function() { 36682 if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) { 36683 this.play(); 36684 } 36685 }, this), 36686 'touchstart.owl.core': $.proxy(function() { 36687 if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) { 36688 this.pause(); 36689 } 36690 }, this), 36691 'touchend.owl.core': $.proxy(function() { 36692 if (this._core.settings.autoplayHoverPause) { 36693 this.play(); 36694 } 36695 }, this) 36696 }; 36697 36698 // register event handlers 36699 this._core.$element.on(this._handlers); 36700 36701 // set default options 36702 this._core.options = $.extend({}, Autoplay.Defaults, this._core.options); 36703 }; 36704 36705 /** 36706 * Default options. 36707 * @public 36708 */ 36709 Autoplay.Defaults = { 36710 autoplay: false, 36711 autoplayTimeout: 5000, 36712 autoplayHoverPause: false, 36713 autoplaySpeed: false 36714 }; 36715 36716 /** 36717 * Starts the autoplay. 36718 * @public 36719 * @param {Number} [timeout] - The interval before the next animation starts. 36720 * @param {Number} [speed] - The animation speed for the animations. 36721 */ 36722 Autoplay.prototype.play = function(timeout, speed) { 36723 this._paused = false; 36724 36725 if (this._core.is('rotating')) { 36726 return; 36727 } 36728 36729 this._core.enter('rotating'); 36730 36731 this._setAutoPlayInterval(); 36732 }; 36733 36734 /** 36735 * Gets a new timeout 36736 * @private 36737 * @param {Number} [timeout] - The interval before the next animation starts. 36738 * @param {Number} [speed] - The animation speed for the animations. 36739 * @return {Timeout} 36740 */ 36741 Autoplay.prototype._getNextTimeout = function(timeout, speed) { 36742 if ( this._timeout ) { 36743 window.clearTimeout(this._timeout); 36744 } 36745 return window.setTimeout($.proxy(function() { 36746 if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) { 36747 return; 36748 } 36749 this._core.next(speed || this._core.settings.autoplaySpeed); 36750 }, this), timeout || this._core.settings.autoplayTimeout); 36751 }; 36752 36753 /** 36754 * Sets autoplay in motion. 36755 * @private 36756 */ 36757 Autoplay.prototype._setAutoPlayInterval = function() { 36758 this._timeout = this._getNextTimeout(); 36759 }; 36760 36761 /** 36762 * Stops the autoplay. 36763 * @public 36764 */ 36765 Autoplay.prototype.stop = function() { 36766 if (!this._core.is('rotating')) { 36767 return; 36768 } 36769 36770 window.clearTimeout(this._timeout); 36771 this._core.leave('rotating'); 36772 }; 36773 36774 /** 36775 * Stops the autoplay. 36776 * @public 36777 */ 36778 Autoplay.prototype.pause = function() { 36779 if (!this._core.is('rotating')) { 36780 return; 36781 } 36782 36783 this._paused = true; 36784 }; 36785 36786 /** 36787 * Destroys the plugin. 36788 */ 36789 Autoplay.prototype.destroy = function() { 36790 var handler, property; 36791 36792 this.stop(); 36793 36794 for (handler in this._handlers) { 36795 this._core.$element.off(handler, this._handlers[handler]); 36796 } 36797 for (property in Object.getOwnPropertyNames(this)) { 36798 typeof this[property] != 'function' && (this[property] = null); 36799 } 36800 }; 36801 36802 $.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay; 36803 36804})(window.Zepto || window.jQuery, window, document); 36805 36806/** 36807 * Navigation Plugin 36808 * @version 2.1.0 36809 * @author Artus Kolanowski 36810 * @author David Deutsch 36811 * @license The MIT License (MIT) 36812 */ 36813;(function($, window, document, undefined) { 36814 'use strict'; 36815 36816 /** 36817 * Creates the navigation plugin. 36818 * @class The Navigation Plugin 36819 * @param {Owl} carousel - The Owl Carousel. 36820 */ 36821 var Navigation = function(carousel) { 36822 /** 36823 * Reference to the core. 36824 * @protected 36825 * @type {Owl} 36826 */ 36827 this._core = carousel; 36828 36829 /** 36830 * Indicates whether the plugin is initialized or not. 36831 * @protected 36832 * @type {Boolean} 36833 */ 36834 this._initialized = false; 36835 36836 /** 36837 * The current paging indexes. 36838 * @protected 36839 * @type {Array} 36840 */ 36841 this._pages = []; 36842 36843 /** 36844 * All DOM elements of the user interface. 36845 * @protected 36846 * @type {Object} 36847 */ 36848 this._controls = {}; 36849 36850 /** 36851 * Markup for an indicator. 36852 * @protected 36853 * @type {Array.<String>} 36854 */ 36855 this._templates = []; 36856 36857 /** 36858 * The carousel element. 36859 * @type {jQuery} 36860 */ 36861 this.$element = this._core.$element; 36862 36863 /** 36864 * Overridden methods of the carousel. 36865 * @protected 36866 * @type {Object} 36867 */ 36868 this._overrides = { 36869 next: this._core.next, 36870 prev: this._core.prev, 36871 to: this._core.to 36872 }; 36873 36874 /** 36875 * All event handlers. 36876 * @protected 36877 * @type {Object} 36878 */ 36879 this._handlers = { 36880 'prepared.owl.carousel': $.proxy(function(e) { 36881 if (e.namespace && this._core.settings.dotsData) { 36882 this._templates.push('<div class="' + this._core.settings.dotClass + '">' + 36883 $(e.content).find('[data-dot]').addBack('[data-dot]').attr('data-dot') + '</div>'); 36884 } 36885 }, this), 36886 'added.owl.carousel': $.proxy(function(e) { 36887 if (e.namespace && this._core.settings.dotsData) { 36888 this._templates.splice(e.position, 0, this._templates.pop()); 36889 } 36890 }, this), 36891 'remove.owl.carousel': $.proxy(function(e) { 36892 if (e.namespace && this._core.settings.dotsData) { 36893 this._templates.splice(e.position, 1); 36894 } 36895 }, this), 36896 'changed.owl.carousel': $.proxy(function(e) { 36897 if (e.namespace && e.property.name == 'position') { 36898 this.draw(); 36899 } 36900 }, this), 36901 'initialized.owl.carousel': $.proxy(function(e) { 36902 if (e.namespace && !this._initialized) { 36903 this._core.trigger('initialize', null, 'navigation'); 36904 this.initialize(); 36905 this.update(); 36906 this.draw(); 36907 this._initialized = true; 36908 this._core.trigger('initialized', null, 'navigation'); 36909 } 36910 }, this), 36911 'refreshed.owl.carousel': $.proxy(function(e) { 36912 if (e.namespace && this._initialized) { 36913 this._core.trigger('refresh', null, 'navigation'); 36914 this.update(); 36915 this.draw(); 36916 this._core.trigger('refreshed', null, 'navigation'); 36917 } 36918 }, this) 36919 }; 36920 36921 // set default options 36922 this._core.options = $.extend({}, Navigation.Defaults, this._core.options); 36923 36924 // register event handlers 36925 this.$element.on(this._handlers); 36926 }; 36927 36928 /** 36929 * Default options. 36930 * @public 36931 * @todo Rename `slideBy` to `navBy` 36932 */ 36933 Navigation.Defaults = { 36934 nav: false, 36935 navText: [ 'prev', 'next' ], 36936 navSpeed: false, 36937 navElement: 'div', 36938 navContainer: false, 36939 navContainerClass: 'owl-nav', 36940 navClass: [ 'owl-prev', 'owl-next' ], 36941 slideBy: 1, 36942 dotClass: 'owl-dot', 36943 dotsClass: 'owl-dots', 36944 dots: true, 36945 dotsEach: false, 36946 dotsData: false, 36947 dotsSpeed: false, 36948 dotsContainer: false 36949 }; 36950 36951 /** 36952 * Initializes the layout of the plugin and extends the carousel. 36953 * @protected 36954 */ 36955 Navigation.prototype.initialize = function() { 36956 var override, 36957 settings = this._core.settings; 36958 36959 // create DOM structure for relative navigation 36960 this._controls.$relative = (settings.navContainer ? $(settings.navContainer) 36961 : $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled'); 36962 36963 this._controls.$previous = $('<' + settings.navElement + '>') 36964 .addClass(settings.navClass[0]) 36965 .html(settings.navText[0]) 36966 .prependTo(this._controls.$relative) 36967 .on('click', $.proxy(function(e) { 36968 this.prev(settings.navSpeed); 36969 }, this)); 36970 this._controls.$next = $('<' + settings.navElement + '>') 36971 .addClass(settings.navClass[1]) 36972 .html(settings.navText[1]) 36973 .appendTo(this._controls.$relative) 36974 .on('click', $.proxy(function(e) { 36975 this.next(settings.navSpeed); 36976 }, this)); 36977 36978 // create DOM structure for absolute navigation 36979 if (!settings.dotsData) { 36980 this._templates = [ $('<div>') 36981 .addClass(settings.dotClass) 36982 .append($('<span>')) 36983 .prop('outerHTML') ]; 36984 } 36985 36986 this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer) 36987 : $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled'); 36988 36989 this._controls.$absolute.on('click', 'div', $.proxy(function(e) { 36990 var index = $(e.target).parent().is(this._controls.$absolute) 36991 ? $(e.target).index() : $(e.target).parent().index(); 36992 36993 e.preventDefault(); 36994 36995 this.to(index, settings.dotsSpeed); 36996 }, this)); 36997 36998 // override public methods of the carousel 36999 for (override in this._overrides) { 37000 this._core[override] = $.proxy(this[override], this); 37001 } 37002 }; 37003 37004 /** 37005 * Destroys the plugin. 37006 * @protected 37007 */ 37008 Navigation.prototype.destroy = function() { 37009 var handler, control, property, override; 37010 37011 for (handler in this._handlers) { 37012 this.$element.off(handler, this._handlers[handler]); 37013 } 37014 for (control in this._controls) { 37015 this._controls[control].remove(); 37016 } 37017 for (override in this.overides) { 37018 this._core[override] = this._overrides[override]; 37019 } 37020 for (property in Object.getOwnPropertyNames(this)) { 37021 typeof this[property] != 'function' && (this[property] = null); 37022 } 37023 }; 37024 37025 /** 37026 * Updates the internal state. 37027 * @protected 37028 */ 37029 Navigation.prototype.update = function() { 37030 var i, j, k, 37031 lower = this._core.clones().length / 2, 37032 upper = lower + this._core.items().length, 37033 maximum = this._core.maximum(true), 37034 settings = this._core.settings, 37035 size = settings.center || settings.autoWidth || settings.dotsData 37036 ? 1 : settings.dotsEach || settings.items; 37037 37038 if (settings.slideBy !== 'page') { 37039 settings.slideBy = Math.min(settings.slideBy, settings.items); 37040 } 37041 37042 if (settings.dots || settings.slideBy == 'page') { 37043 this._pages = []; 37044 37045 for (i = lower, j = 0, k = 0; i < upper; i++) { 37046 if (j >= size || j === 0) { 37047 this._pages.push({ 37048 start: Math.min(maximum, i - lower), 37049 end: i - lower + size - 1 37050 }); 37051 if (Math.min(maximum, i - lower) === maximum) { 37052 break; 37053 } 37054 j = 0, ++k; 37055 } 37056 j += this._core.mergers(this._core.relative(i)); 37057 } 37058 } 37059 }; 37060 37061 /** 37062 * Draws the user interface. 37063 * @todo The option `dotsData` wont work. 37064 * @protected 37065 */ 37066 Navigation.prototype.draw = function() { 37067 var difference, 37068 settings = this._core.settings, 37069 disabled = this._core.items().length <= settings.items, 37070 index = this._core.relative(this._core.current()), 37071 loop = settings.loop || settings.rewind; 37072 37073 this._controls.$relative.toggleClass('disabled', !settings.nav || disabled); 37074 37075 if (settings.nav) { 37076 this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true)); 37077 this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true)); 37078 } 37079 37080 this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled); 37081 37082 if (settings.dots) { 37083 difference = this._pages.length - this._controls.$absolute.children().length; 37084 37085 if (settings.dotsData && difference !== 0) { 37086 this._controls.$absolute.html(this._templates.join('')); 37087 } else if (difference > 0) { 37088 this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0])); 37089 } else if (difference < 0) { 37090 this._controls.$absolute.children().slice(difference).remove(); 37091 } 37092 37093 this._controls.$absolute.find('.active').removeClass('active'); 37094 this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active'); 37095 } 37096 }; 37097 37098 /** 37099 * Extends event data. 37100 * @protected 37101 * @param {Event} event - The event object which gets thrown. 37102 */ 37103 Navigation.prototype.onTrigger = function(event) { 37104 var settings = this._core.settings; 37105 37106 event.page = { 37107 index: $.inArray(this.current(), this._pages), 37108 count: this._pages.length,
vendor: 7,458 bytes, lines 37109-37390
37109 size: settings && (settings.center || settings.autoWidth || settings.dotsData 37110 ? 1 : settings.dotsEach || settings.items) 37111 }; 37112 }; 37113 37114 /** 37115 * Gets the current page position of the carousel. 37116 * @protected 37117 * @returns {Number} 37118 */ 37119 Navigation.prototype.current = function() { 37120 var current = this._core.relative(this._core.current()); 37121 return $.grep(this._pages, $.proxy(function(page, index) { 37122 return page.start <= current && page.end >= current; 37123 }, this)).pop(); 37124 }; 37125 37126 /** 37127 * Gets the current succesor/predecessor position. 37128 * @protected 37129 * @returns {Number} 37130 */ 37131 Navigation.prototype.getPosition = function(successor) { 37132 var position, length, 37133 settings = this._core.settings; 37134 37135 if (settings.slideBy == 'page') { 37136 position = $.inArray(this.current(), this._pages); 37137 length = this._pages.length; 37138 successor ? ++position : --position; 37139 position = this._pages[((position % length) + length) % length].start; 37140 } else { 37141 position = this._core.relative(this._core.current()); 37142 length = this._core.items().length; 37143 successor ? position += settings.slideBy : position -= settings.slideBy; 37144 } 37145 37146 return position; 37147 }; 37148 37149 /** 37150 * Slides to the next item or page. 37151 * @public 37152 * @param {Number} [speed=false] - The time in milliseconds for the transition. 37153 */ 37154 Navigation.prototype.next = function(speed) { 37155 $.proxy(this._overrides.to, this._core)(this.getPosition(true), speed); 37156 }; 37157 37158 /** 37159 * Slides to the previous item or page. 37160 * @public 37161 * @param {Number} [speed=false] - The time in milliseconds for the transition. 37162 */ 37163 Navigation.prototype.prev = function(speed) { 37164 $.proxy(this._overrides.to, this._core)(this.getPosition(false), speed); 37165 }; 37166 37167 /** 37168 * Slides to the specified item or page. 37169 * @public 37170 * @param {Number} position - The position of the item or page. 37171 * @param {Number} [speed] - The time in milliseconds for the transition. 37172 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not. 37173 */ 37174 Navigation.prototype.to = function(position, speed, standard) { 37175 var length; 37176 37177 if (!standard && this._pages.length) { 37178 length = this._pages.length; 37179 $.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed); 37180 } else { 37181 $.proxy(this._overrides.to, this._core)(position, speed); 37182 } 37183 }; 37184 37185 $.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation; 37186 37187})(window.Zepto || window.jQuery, window, document); 37188 37189/** 37190 * Hash Plugin 37191 * @version 2.1.0 37192 * @author Artus Kolanowski 37193 * @author David Deutsch 37194 * @license The MIT License (MIT) 37195 */ 37196;(function($, window, document, undefined) { 37197 'use strict'; 37198 37199 /** 37200 * Creates the hash plugin. 37201 * @class The Hash Plugin 37202 * @param {Owl} carousel - The Owl Carousel 37203 */ 37204 var Hash = function(carousel) { 37205 /** 37206 * Reference to the core. 37207 * @protected 37208 * @type {Owl} 37209 */ 37210 this._core = carousel; 37211 37212 /** 37213 * Hash index for the items. 37214 * @protected 37215 * @type {Object} 37216 */ 37217 this._hashes = {}; 37218 37219 /** 37220 * The carousel element. 37221 * @type {jQuery} 37222 */ 37223 this.$element = this._core.$element; 37224 37225 /** 37226 * All event handlers. 37227 * @protected 37228 * @type {Object} 37229 */ 37230 this._handlers = { 37231 'initialized.owl.carousel': $.proxy(function(e) { 37232 if (e.namespace && this._core.settings.startPosition === 'URLHash') { 37233 $(window).trigger('hashchange.owl.navigation'); 37234 } 37235 }, this), 37236 'prepared.owl.carousel': $.proxy(function(e) { 37237 if (e.namespace) { 37238 var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash'); 37239 37240 if (!hash) { 37241 return; 37242 } 37243 37244 this._hashes[hash] = e.content; 37245 } 37246 }, this), 37247 'changed.owl.carousel': $.proxy(function(e) { 37248 if (e.namespace && e.property.name === 'position') { 37249 var current = this._core.items(this._core.relative(this._core.current())), 37250 hash = $.map(this._hashes, function(item, hash) { 37251 return item === current ? hash : null; 37252 }).join(); 37253 37254 if (!hash || window.location.hash.slice(1) === hash) { 37255 return; 37256 } 37257 37258 window.location.hash = hash; 37259 } 37260 }, this) 37261 }; 37262 37263 // set default options 37264 this._core.options = $.extend({}, Hash.Defaults, this._core.options); 37265 37266 // register the event handlers 37267 this.$element.on(this._handlers); 37268 37269 // register event listener for hash navigation 37270 $(window).on('hashchange.owl.navigation', $.proxy(function(e) { 37271 var hash = window.location.hash.substring(1), 37272 items = this._core.$stage.children(), 37273 position = this._hashes[hash] && items.index(this._hashes[hash]); 37274 37275 if (position === undefined || position === this._core.current()) { 37276 return; 37277 } 37278 37279 this._core.to(this._core.relative(position), false, true); 37280 }, this)); 37281 }; 37282 37283 /** 37284 * Default options. 37285 * @public 37286 */ 37287 Hash.Defaults = { 37288 URLhashListener: false 37289 }; 37290 37291 /** 37292 * Destroys the plugin. 37293 * @public 37294 */ 37295 Hash.prototype.destroy = function() { 37296 var handler, property; 37297 37298 $(window).off('hashchange.owl.navigation'); 37299 37300 for (handler in this._handlers) { 37301 this._core.$element.off(handler, this._handlers[handler]); 37302 } 37303 for (property in Object.getOwnPropertyNames(this)) { 37304 typeof this[property] != 'function' && (this[property] = null); 37305 } 37306 }; 37307 37308 $.fn.owlCarousel.Constructor.Plugins.Hash = Hash; 37309 37310})(window.Zepto || window.jQuery, window, document); 37311 37312/** 37313 * Support Plugin 37314 * 37315 * @version 2.1.0 37316 * @author Vivid Planet Software GmbH 37317 * @author Artus Kolanowski 37318 * @author David Deutsch 37319 * @license The MIT License (MIT) 37320 */ 37321;(function($, window, document, undefined) { 37322 37323 var style = $('<support>').get(0).style, 37324 prefixes = 'Webkit Moz O ms'.split(' '), 37325 events = { 37326 transition: { 37327 end: { 37328 WebkitTransition: 'webkitTransitionEnd', 37329 MozTransition: 'transitionend', 37330 OTransition: 'oTransitionEnd', 37331 transition: 'transitionend' 37332 } 37333 }, 37334 animation: { 37335 end: { 37336 WebkitAnimation: 'webkitAnimationEnd', 37337 MozAnimation: 'animationend', 37338 OAnimation: 'oAnimationEnd', 37339 animation: 'animationend' 37340 } 37341 } 37342 }, 37343 tests = { 37344 csstransforms: function() { 37345 return !!test('transform'); 37346 }, 37347 csstransforms3d: function() { 37348 return !!test('perspective'); 37349 }, 37350 csstransitions: function() { 37351 return !!test('transition'); 37352 }, 37353 cssanimations: function() { 37354 return !!test('animation'); 37355 } 37356 }; 37357 37358 function test(property, prefixed) { 37359 var result = false, 37360 upper = property.charAt(0).toUpperCase() + property.slice(1); 37361 37362 $.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) { 37363 if (style[property] !== undefined) { 37364 result = prefixed ? property : true; 37365 return false; 37366 } 37367 }); 37368 37369 return result; 37370 } 37371 37372 function prefixed(property) { 37373 return test(property, true); 37374 } 37375 37376 if (tests.csstransitions()) { 37377 /* jshint -W053 */ 37378 $.support.transition = new String(prefixed('transition')) 37379 $.support.transition.end = events.transition.end[ $.support.transition ]; 37380 } 37381 37382 if (tests.cssanimations()) { 37383 /* jshint -W053 */ 37384 $.support.animation = new String(prefixed('animation')) 37385 $.support.animation.end = events.animation.end[ $.support.animation ]; 37386 } 37387 37388 if (tests.csstransforms()) { 37389 /* jshint -W053 */ 37390 $.support.transform = new String(prefixed('transform'));
37391 $.support.transform3d = tests.csstransforms3d(); 37392 } 37393 37394})(window.Zepto || window.jQuery, window, document); 37395 37396/* Global */ 37397var theplayer; 37398var wWidth = $(window).width(); 37399var wHeight = $(window).height(); 37400 37401var frontendMaster = new function () { 37402 37403 this.init = function () { 37404 frontendMaster.accessoriesSlider(); 37405 frontendMaster.topNavigation(); 37406 frontendMaster.interchangeImages(); 37407 frontendMaster.bootstrapFormHelpers(); 37408 frontendMaster.downloadButtonFix(); 37409 frontendMaster.getInTouchBanner(); 37410 frontendMaster.latest3News(); 37411 //frontendMaster.singleItemCarousel(); 37412 frontendMaster.wholeWideCarousel(); 37413 frontendMaster.commentsSlider(); 37414 frontendMaster.productGallery(); 37415 frontendMaster.simpleSlider(); 37416 frontendMaster.productFiltersGroup(); 37417 frontendMaster.productTableTabs(); 37418 frontendMaster.productAccessoriesGallery(); 37419 }; 37420 37421 37422 this.accessoriesSlider = function () { 37423 var owl = $('.accessories-slider'); 37424 if (owl.length > 0) 37425 { 37426 owl.owlCarousel({ 37427 loop: false, 37428 margin: 10, 37429 navRewind: false, 37430 rewind: false, 37431 nav: true, 37432 dots: false, 37433 responsive: { 37434 0: { 37435 items: 1 37436 }, 37437 600: { 37438 items: 3 37439 }, 37440 1000: { 37441 items: 6 37442 } 37443 } 37444 }); 37445 } 37446 37447 } 37448 37449 37450 this.interchangeImages = function () { 37451 /* Markup example: 5:2 or 8:5 aspect ratio, 37452 <div class="is-fullpage interchange-images" data-image-sm="http://placehold.it/640x480" data-image-md="http://placehold.it/1024x410" data-image-lg="./images/Topbillede-1680x480-5.jpg" data-image-sm-fullpage="http://placehold.it/640x480" data-image-md-fullpage="http://placehold.it/1024x640" data-image-lg-fullpage="http://placehold.it/1920x1200"> 37453 */ 37454 //Dependency enquire.js http://wicky.nillia.ms/enquire.js/#v2.0.0 37455 var querySm = "screen and (min-width:0px) and (max-width:767px)"; 37456 var queryMd = "screen and (min-width:768px) and (max-width:1024px)"; 37457 var queryLg = "screen and (min-width:1025px)"; 37458 //interchange responsive images for different screens sizes 37459 $(".interchange-images").each(function (intIndex) { 37460 var thisModule = $(this); 37461 var imageUrlSm = $(this).attr('data-image-sm'); 37462 var imageUrlMd = $(this).attr('data-image-md'); 37463 var imageUrlLg = $(this).attr('data-image-lg'); 37464 var imageUrlSmFullpage = $(this).attr('data-image-sm-fullpage'); 37465 var imageUrlMdFullpage = $(this).attr('data-image-md-fullpage'); 37466 var imageUrlLgFullpage = $(this).attr('data-image-lg-fullpage'); 37467 //Load normal or fullpage bg images 37468 if ($(thisModule).hasClass("is-fullpage-slider")) { 37469 enquire 37470 .register(querySm, { 37471 match: function () { 37472 //console.log("SMALL handler 1 matched"); 37473 $(thisModule).css("background-image", "url(" + imageUrlSmFullpage + ")"); 37474 }, 37475 unmatch: function () { 37476 } 37477 }) 37478 .register(queryMd, { 37479 match: function () { 37480 //console.log("MEDIUM handler 2 matched"); 37481 $(thisModule).css("background-image", "url(" + imageUrlMdFullpage + ")"); 37482 }, 37483 unmatch: function () { 37484 } 37485 }) 37486 .register(queryLg, { 37487 match: function () { 37488 //console.log("LARGE handler 3 matched"); 37489 $(thisModule).css("background-image", "url(" + imageUrlLgFullpage + ")"); 37490 }, 37491 unmatch: function () { 37492 } 37493 }, true); 37494 } 37495 else { 37496 enquire 37497 .register(querySm, { 37498 match: function () { 37499 //console.log("SMALL handler 1 matched"); 37500 $(thisModule).css("background-image", "url(" + imageUrlSm + ")"); 37501 }, 37502 unmatch: function () { 37503 } 37504 }) 37505 .register(queryMd, { 37506 match: function () { 37507 //console.log("MEDIUM handler 2 matched"); 37508 $(thisModule).css("background-image", "url(" + imageUrlMd + ")"); 37509 }, 37510 unmatch: function () { 37511 } 37512 }) 37513 .register(queryLg, { 37514 match: function () { 37515 //console.log("LARGE handler 3 matched"); 37516 $(thisModule).css("background-image", "url(" + imageUrlLg + ")"); 37517 }, 37518 unmatch: function () { 37519 } 37520 }, true); 37521 } 37522 //fadein...
37523 $(thisModule).addClass("loaded"); 37524 }); 37525 }; 37526 37527 this.singleItemCarousel = function () { 37528 //Requires Slick slider 37529 $('.single-item-carousel').slick({ 37530 dots: true, 37531 infinite: true, 37532 speed: 400, 37533 lazyLoad: 'ondemand', 37534 autoplay: true, 37535 autoplaySpeed: 3000, 37536 accessibility: true, 37537 cssEase: 'easeOutBack', 37538 prevArrow: '<button class="btn prev" aria-label="previous" data-role="none" type="button"><i class="fa fa-chevron-left"></i></button>', 37539 nextArrow: '<button class="btn next" aria-label="next" data-role="none" type="button"><i class="fa fa-chevron-right"></i></button>', 37540 pauseOnHover: false 37541 }); 37542 }; 37543 37544 this.productGallery = function () { 37545 $('.product-gallery').royalSlider({ 37546 autoHeight: true, 37547 arrowsNav: true, 37548 arrowsNavAutoHide: true, 37549 arrowsNavHideOnTouch: false, 37550 keyboardNavEnabled: true, 37551 fadeinLoadedSlide: true, 37552 controlNavigationSpacing: 0, 37553 controlNavigation: 'thumbnails', 37554 imageScaleMode: 'none', 37555 imageAlignCenter: true, 37556 loop: false, 37557 loopRewind: true, 37558 navigateByClick: true, 37559 numImagesToPreload: 2, 37560 usePreloader: true, 37561 slidesSpacing: 0, 37562 fullscreen: { 37563 enabled: true, 37564 keyboardNav: true, 37565 nativeFS: true 37566 }, 37567 thumbs: { 37568 autoCenter: false, 37569 fitInViewPort: true, 37570 spacing: 4 37571 } 37572 }); 37573 }; 37574 37575 this.simpleSlider = function () { 37576 var slider = $('.simple-slider'); 37577 if (slider.length > 0) { 37578 37579 $('.simple-slider').royalSlider({ 37580 autoHeight: true, 37581 arrowsNav: true, 37582 arrowsNavAutoHide: true, 37583 arrowsNavHideOnTouch: false, 37584 keyboardNavEnabled: true, 37585 fadeinLoadedSlide: true, 37586 controlNavigationSpacing: 0, 37587 controlNavigation: 'thumbnails', 37588 imageScaleMode: 'none', 37589 imageAlignCenter: true, 37590 loop: false, 37591 loopRewind: true, 37592 navigateByClick: false, 37593 numImagesToPreload: 2, 37594 usePreloader: true, 37595 slidesSpacing: 0, 37596 fullscreen: { 37597 enabled: true, 37598 keyboardNav: true, 37599 nativeFS: true 37600 }, 37601 thumbs: { 37602 autoCenter: false, 37603 fitInViewPort: true, 37604 spacing: 4 37605 } 37606 }); 37607 37608 } 37609 }; 37610 37611 this.loadImagesCommentsSlider = function () { 37612 var screenWidth = $(window).width(); 37613 //console.log(screenWidth); 37614 // You may change image URL by modifying rsImg sources before slider initialization 37615 // Loop over each a tag and populate the right size of image.... 37616 var items = $("#customers-slider .rsImg"), itemsNumber = items.length, state = "src"; 37617 if (screenWidth < 640) { 37618 items.each(function (intIndex) { 37619 var image = $(this).parent().attr('data-image-xs'); 37620 $(this).attr(state, image); 37621 }); 37622 } 37623 else { 37624 items.each(function (intIndex) { 37625 var image = $(this).parent().attr('data-image-md'); 37626 $(this).attr(state, image); 37627 }); 37628 } 37629 //If only one item, add class to slider 37630 if (itemsNumber < 2) { 37631 $("#customers-slider").addClass("no-thumbnails"); 37632 } 37633 }; 37634 this.commentsSlider = function () { 37635 frontendMaster.loadImagesCommentsSlider(); 37636 $('#customers-slider').royalSlider({ 37637 controlNavigation: 'thumbnails', 37638 autoScaleSlider: false, 37639 autoScaleSliderWidth: 1500, 37640 autoScaleSliderHeight: 480, 37641 loop: true, 37642 imageScaleMode: 'none', 37643 navigateByClick: true, 37644 numImagesToPreload: 2, 37645 arrowsNav: false, 37646 arrowsNavAutoHide: false, 37647 arrowsNavHideOnTouch: false, 37648 keyboardNavEnabled: true, 37649 fadeinLoadedSlide: true, 37650 globalCaption: true, 37651 globalCaptionInside: true, 37652 slidesSpacing: 0, 37653 thumbs: { 37654 //appendSpan: true,
37655 arrows: false, 37656 firstMargin: true, 37657 paddingTop: 0, 37658 spacing: 15 37659 } 37660 }); 37661 var slider = $("#customers-slider.royalSlider").data("royalSlider"); 37662 //prev + next custom btns 37663 $("#customers-slider + .custom-btns .prev-btn").click(function () { 37664 slider.prev(); 37665 }); 37666 $("#customers-slider + .custom-btns .next-btn").click(function () { 37667 slider.next(); 37668 }); 37669 //fadein when is loaded... 37670 $("#customers-slider").addClass("on"); 37671 }; 37672 this.downloadButtonFix = function () { 37673 //hey markup from RTE is &/%/&%/, here is a fix 37674 $(".download").each(function (intIndex) { 37675 $(this).find("a").addClass("hd-btn btn-1 btn-1c btn-md"); 37676 }); 37677 //extra fix for icons buttons 37678 $(".hd-btn.btn-1.btn-1c").each(function (intIndex) { 37679 $(this).find("i").parent().addClass("btn-icon"); 37680 }); 37681 }; 37682 this.lazyloadingImages = function () { 37683 //requires the jquery.lazyload.min.js plugin 37684 //<img class="lazy" data-original="img/example.jpg" width="765" height="574"> 37685 $("img.lazy").lazyload({ 37686 effect: "fadeIn" 37687 }); 37688 }; 37689 this.productFiltersGroup = function () { 37690 $(".product-filters-group h3").click(function () { 37691 $(this).closest(".product-filters-group").toggleClass("opened"); 37692 }); 37693 }; 37694 this.wholeWideCarousel = function () { 37695 $('.whole-wide-carousel').slick({ 37696 dots: false, 37697 infinite: true, 37698 speed: 300, 37699 slidesToShow: 3, 37700 slidesToScroll: 1, 37701 lazyLoad: 'ondemand', 37702 autoplay: true, 37703 autoplaySpeed: 2000, 37704 prevArrow: '<button class="btn prev" aria-label="previous" data-role="none" type="button"><i class="fa fa-chevron-left"></i></button>', 37705 nextArrow: '<button class="btn next" aria-label="next" data-role="none" type="button"><i class="fa fa-chevron-right"></i></button>', 37706 responsive: [ 37707 { 37708 breakpoint: 1024, 37709 settings: { 37710 slidesToShow: 3, 37711 slidesToScroll: 3, 37712 infinite: true, 37713 dots: false 37714 } 37715 }, 37716 { 37717 breakpoint: 600, 37718 settings: { 37719 slidesToShow: 2, 37720 slidesToScroll: 2, 37721 dots: false 37722 } 37723 }, 37724 { 37725 breakpoint: 480, 37726 settings: { 37727 slidesToShow: 1, 37728 slidesToScroll: 1, 37729 dots: false 37730 } 37731 } 37732 ] 37733 }); 37734 //fade in when ready... 37735 $(".whole-wide-carousel").addClass("ready"); 37736 }; 37737 this.latest3News = function () { 37738 //on hover open news box 37739 $(".no-touch .latest-3-news .news-box").mouseenter(function () { 37740 $(this).parent().addClass("hover"); 37741 $(this).parent().siblings().removeClass("hover"); 37742 }); 37743 //open the first newsbox by default 37744 $(".no-touch .latest-3-news .col-md-12 + .col-md-6").addClass("hover"); 37745 }; 37746 this.topHeaderScroll = function () { 37747 37748 if (!$('html').hasClass('lt-ie9')) { 37749 //Make the top header slimmer when scrolled pass by it 37750 function yScroll() { 37751 var pagetop = $("#top"), yPos = window.pageYOffset; 37752 if (yPos > 30) { 37753 pagetop.addClass("slim-header"); 37754 } 37755 else { 37756 pagetop.removeClass("slim-header"); 37757 } 37758 } 37759 if ($('html').hasClass("no-touch")) { 37760 window.addEventListener("scroll", yScroll); 37761 } 37762 } 37763 }; 37764 this.topHeaderMegadropdown = function () { 37765 //open megadropdown 37766 // add .non-dropdown-trigger for links that do not need to open a dropdown 37767 $("#desktop-nav-01 > li > a").click(function () { 37768 var subnav = $(this).attr("data-dropdown-id"); 37769 if (subnav != '') { 37770 //if data-dropdown-id is not empty, open its dropdown 37771 subnav = "#" + subnav; 37772 $(subnav).slideToggle("fast"); //open active dropdown 37773 $(subnav).siblings().slideUp("fast"); //closing megadropdowns 37774 $(this).parent().siblings().removeClass("active"); //remove .active from all sibling links 37775 $(this).parent().toggleClass("active"); //add active class 37776 return false; 37777 } 37778 });
37779 2//close all subnavs on .close-subnav click 37780 $(".close-subnav").click(function () { 37781 $("#mega-dropdown > div > div").slideUp("fast"); 37782 $("#desktop-nav-01 > li").removeClass("active"); 37783 return false; 37784 }); 37785 }; 37786 this.mobileMenu = function () { 37787 //Mobile Menu..................................... 37788 $('.burger-btn').click(function () { 37789 $(this).toggleClass('open'); 37790 $("#top, html").toggleClass('mobilemenu-open'); 37791 }); 37792 //open/close mobile menu dropdowns... 37793 $("#mobile-nav .has-children > a").click(function () { 37794 //open close dropdowns 37795 $(this).parent().toggleClass("active"); 37796 $(this).toggleClass("active"); 37797 //close siblings dropdowns 37798 $(this).parent().siblings().removeClass("active"); 37799 return false; 37800 }); 37801 }; 37802 this.topNavigation = function () { 37803 frontendMaster.topHeaderScroll(); 37804 frontendMaster.topHeaderMegadropdown(); 37805 frontendMaster.mobileMenu(); 37806 }; 37807 this.bootstrapFormHelpers = function () { 37808 //requires bootstrap select dependency: (http://silviomoreto.github.io/bootstrap-select/) 37809 //Enable mobile scrolling. This enables the device's native menu for select menus. 37810 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) { 37811 $('.selectpicker').selectpicker('mobile'); 37812 } 37813 else { 37814 $('.selectpicker').selectpicker(); 37815 } 37816 }; 37817 this.getInTouchBanner = function () { 37818 //kontakt form 37819 $('.open-contact-form').click(function () { 37820 //open contact form + hide some buttons... 37821 $(this).closest(".get-in-touch-banner").toggleClass("open-form"); 37822 $(".get-in-touch-banner + #contact-form").slideToggle("fast"); 37823 }); 37824 //close kontakt form 37825 $('.get-in-touch-banner .fa.fa-times, .get-in-touch-banner .close-btn').click(function () { 37826 $(this).closest(".get-in-touch-banner").removeClass("open-form"); 37827 $(".get-in-touch-banner + #contact-form").slideUp("fast"); 37828 return false; 37829 }); 37830 }; 37831 37832 this.productTableTabs = function () { 37833 // Only trigger on dynamic product pages 37834 $(".product-module").each(function () { 37835 $currentModule = $(this); 37836 37837 // Find the table with tabs container 37838 $currentModule.find(".product-tab-list").each(function () { 37839 $currentTabList = $(this); 37840 37841 // Setup tabs clicks -> 37842 // Get references to both the button, and the content which should be linked to the button when clicked 37843 var $tabButtons = $currentTabList.find(".tab-button-container button"); 37844 var $tabContent = $currentTabList.find(".tab-content-list .tab-container"); 37845 37846 $tabButtons.on("click", function (evt) { 37847 evt.preventDefault(); 37848 var $clickedButton = $(this); 37849 37850 // Which no button has been clicked. The first? (index 0) or perhaps the second (index 1) ... 37851 var targetIndex = $tabButtons.index($clickedButton); 37852 37853 // Remove active class on all buttons and content containers 37854 // Then add it again on the selected. Start with content, as users eyes will have focus on it .. 37855 $tabContent.removeClass("active"); 37856 $($tabContent[targetIndex]).addClass("active"); 37857 37858 $tabButtons.removeClass("active"); 37859 $clickedButton.addClass("active"); 37860 }); 37861 37862 // Setup mm/inch toggle -> 37863 var $unitButtons = $currentTabList.find(".unit-button-container button"); 37864 $unitButtons.on("click", function (evt) { 37865 evt.preventDefault(); 37866 var $clickedButton = $(this); 37867 var target = $clickedButton.data("unittype-target"); 37868 var unitContainers = $tabContent.find("span[data-unittype]"); 37869 unitContainers.hide(); 37870 unitContainers.filter("[data-unittype=" + target + "]").show(); 37871 37872 $unitButtons.removeClass("active"); 37873 $clickedButton.addClass("active"); 37874 }); 37875 }); 37876 }); 37877 }; 37878 37879 // This will toggle the larger infobox for each accessory of a product when clicked 37880 this.productAccessoriesGallery = function () { 37881 $(".product-module .accessories-module").each(function () { 37882 // Settings 37883 $accessoryModule = $(this); 37884 animationSpeed = 800; 37885 37886 // Find the list of thumbnails
37887 var $accessoryThumbnails = $accessoryModule.find(".item"); 37888 37889 // Find the container for the thumbnails 37890 var $container = $accessoryModule.find(".more-info-container > .container"); 37891 37892 // When a thumbnail is clicked .. 37893 $accessoryThumbnails.on("click", function () { 37894 $clickedAccessory = $(this); 37895 37896 if (!$clickedAccessory.hasClass('active')) { 37897 37898 // Find its index in the list 37899 var targetIndex = $accessoryThumbnails.index($clickedAccessory); 37900 37901 // Find the list with the complete content for each accessory 37902 var $accessoriesContent = $accessoryModule.find(".more-info-list > li"); 37903 var $selectedAccessoryContent = $($accessoriesContent[targetIndex]); 37904 37905 // Prepare callback for opening the clicked box - we just need to ensure any opened boxes are gently closed first 37906 var fnShowActiveAccessory = function () { 37907 // Set inactive on all contentboxes 37908 $accessoriesContent.removeClass("active"); 37909 37910 // Set active on contentbox which matches clicked thumbnail 37911 $selectedAccessoryContent.addClass("active"); 37912 37913 // Operations has been in the hidden until now - show the result 37914 $container.slideDown(animationSpeed) 37915 }; 37916 37917 // If container is already open, close it before opening the clicked item 37918 if ($container.is(":visible")) 37919 $container.slideUp(fnShowActiveAccessory); 37920 else 37921 fnShowActiveAccessory(); 37922 37923 2 // Hook up the close button 37924 $selectedAccessoryContent.find(".close").on("click", function () { 37925 $container.slideUp(function () { 37926 $selectedAccessoryContent.removeClass("active"); 37927 $clickedAccessory.removeClass("active"); 37928 }); 37929 }); 37930 37931 // Deselect the current selected thumbnail and set the clicked one to active 37932 $accessoryThumbnails.removeClass("active"); 37933 $clickedAccessory.addClass("active"); 37934 } 37935 }); 37936 }); 37937 } 37938}; 37939$(document).ready(function () { 37940 frontendMaster.init(); 37941}); 37942//# sourceMappingURL=frontend.js.map 37943var backendMaster = new function () { 37944 this.init = function () { 37945 this.ensureCompatibility(); 37946 this.postFormByAjax(); 37947 this.setupUmbracoForms();
37948 this.loadMoreImage(); 37949 this.cookieDisclaimer(); 37950 this.internalRedirectOnPage(); 37951 this.parallaxScrolling(); 37952 }; 37953 this.parallaxScrolling = function () { 37954 37955 if ($("html.no-touch").length > 0) { 37956 var controller = new ScrollMagic.Controller({ 37957 globalSceneOptions: { 37958 triggerHook: 'onLeave' 37959 } 37960 }); 37961 var slides = document.querySelectorAll(".parallaxScrolling"); 37962 // create scene for every slide 37963 for (var i = 0; i < slides.length; i++) { 37964 new ScrollMagic.Scene({ 37965 triggerElement: slides[i] 37966 }) 37967 .setPin(slides[i]) 37968 .addTo(controller); 37969 } 37970 } 37971 37972 }, 37973 this.internalRedirectOnPage = function () { 37974 $(document).on('click', 'a[href^="#"]', function (e) { 37975 // target element id 37976 var id = $(this).attr('href'); 37977 // target element 37978 var $id = $(id); 37979 if ($id.length === 0) { 37980 return; 37981 } 37982 // prevent standard hash navigation (avoid blinking in IE) 37983 e.preventDefault(); 37984 // top position relative to the document 37985 var pos = $(id).offset().top; 37986 // animated top scrolling 37987 $('body, html').animate({ scrollTop: pos }); 37988 }); 37989 }, 37990 this.cookieDisclaimer = function () { 37991 $("#cookie-div button").click(function () { 37992 document.cookie = "showcookies=0;path=/"; 37993 $("#cookie-div").remove(); 37994 }); 37995 window.onload = function () { 37996 window.onscroll = function () { 37997 if ($(window).scrollTop() > $(document).height() - $(window).height() - $("footer").height()) { 37998 $("#cookie-div").addClass("scrollactive"); 37999 } 38000 else if ($("footer").position().top <= $(window).height()) { 38001 $("#cookie-div").addClass("scrollactive"); 38002 } 38003 else { 38004 $("#cookie-div").removeClass("scrollactive"); 38005 } 38006 }; 38007 window.onscroll(); 38008 }; 38009 }, 38010 this.loadMoreImage = function () { 38011 $(".loadMoreImage").click(function () { 38012 var items = $(this).parent().find(".otherImages").slice(0, 8); 38013 items.fadeIn(); 38014 items.removeClass("otherImages"); 38015 if ($(".otherImages").length == 0) { 38016 $(".loadMoreImage").hide(); 38017 } 38018 }); 38019 }; 38020 this.postFormByAjax = function () { 38021 $(".call-to-action-banner form").submit(function (e) { 38022 e.preventDefault(); 38023 var form = $(this); 38024 console.log("tetser"); 38025 var service = $(this).data("api"); 38026 var url = "/umbraco/api/" + service; // the script where you handle the form input. 38027 if (form.valid()) { 38028 $.ajax({
38029 type: "POST", 38030 url: url, 38031 data: $(this).serialize(), 38032 success: function (data) { 38033 form.fadeOut(); 38034 form.parent().parent().find(".signUpMessage").fadeIn(); 38035 } 38036 }); 38037 } 38038 return false; // avoid to execute the actual submit of the form. 38039 }); 38040 }; 38041 this.setupUmbracoForms = function () { 38042 // Will only execute, on found elements 38043 // AJAX POST, instead of page-reload 38044 $(".contour form").submit(function (evt) { 38045 var form = $(this); 38046 // Prevent browser from changing page 38047 evt.preventDefault(); 38048 // Validate user input (based on settings from backend) 38049 var isValid = true; 38050 form.find(".textfield.mandatory input[type=text]").each(function (index, input) { 38051 input = $(input); 38052 if (input.val().trim() == "") { 38053 input.css("border", "1px solid Red"); 38054 isValid = false; 38055 } 38056 }); 38057 if (!isValid) 38058 return false; 38059 // Take user data and serialize it 38060 var data = form.serialize(); 38061 // POST to the server (with transitions) 38062 // Replace content of form with a loading-spinner and setup for showing the confirmation message 38063 $.post(form.attr("action"), data, function (response) { 38064 // "response" is the complete HTML markup for a page redirect 38065 //var confirmationMessage = $(response).find(".contourMessageOnSubmit"); 38066 // Save of form is done, show confirmation to user 38067 form.fadeOut(); 38068 form.parent().parent().find(".signUpMessage").fadeIn(); 38069 }); 38070 return false; 38071 }); 38072 }; 38073 this.searchByQueryString = function () { 38074 setTimeout(function () { 38075 var searchFor = backendMaster.getQueryString("search"); 38076 if (searchFor != "" && typeof google !== 'undefined' && google != undefined && google.search != undefined) { 38077 var element = google.search.cse.element.getElement('searchresults-only0'); 38078 if (element == null) { 38079 } 38080 else if (searchFor == '') { 38081 element.clearAllResults(); 38082 } 38083 else { 38084 // Is the first search? Then move container to the requesting form 38085 if ($("#searchResults").length && $("#searchResults:visible")) 38086 $("#searchResults").appendTo(".searchResultContainer").show(); 38087 // Ask Google to show results 38088 element.execute(searchFor); 38089 } 38090 } 38091 }, 1000); 38092 }; 38093 this.startSearch = function (frmSender) { 38094 // Take searched keyword 38095 var input = frmSender.getElementsByTagName("input")[0]; 38096 // Take CSE element (<<gcse:searchresults-only></gcse:searchresults-only>) 38097 if (searchFor != "" && typeof google !== 'undefined' && google != undefined && google.search != undefined) { 38098 var element = google.search.cse.element.getElement('searchresults-only0'); 38099 if (element == null) { 38100 } 38101 else if (input.value == '') { 38102 element.clearAllResults(); 38103 } 38104 else { 38105 // Is the first search? Then move container to the requesting form 38106 if ($("#searchResults").length && $("#searchResults:visible")) 38107 $("#searchResults").appendTo(".searchResultContainer").show(); 38108 // Ask Google to show results 38109 element.execute(input.value); 38110 } 38111 } 38112 return false; 38113 }; 38114 this.getQueryString = function (name) { 38115 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 38116 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search); 38117 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
38118 }; 38119 /* Feature detection and fixes */ 38120 this.ensureCompatibility = function () { 38121 // Simulate placeholder behavoir in browsers without support for it 38122 if (Helper && !Helper.nativePlaceholderSupport()) { 38123 $("input[placeholder]").each(function () { 38124 if ($(this).val() == '') 38125 $(this).val($(this).attr("placeholder")); 38126 $(this).addClass('placeholder'); 38127 }).bind('focus', function () { 38128 if ($(this).val() == $(this).attr('placeholder')) { 38129 $(this).val('').removeClass('placeholder'); 38130 } 38131 }).bind('blur', function () { 38132 if ($(this).val() == '') { 38133 $(this).val($(this).attr("placeholder")).addClass('placeholder'); 38134 } 38135 }); 38136 } 38137 }; 38138}; 38139$(document).ready(function () { 38140 backendMaster.init(); 38141}); 38142//# sourceMappingURL=backend.js.map 38143(function () { 38144 38145 var settings = { 38146 button: '#back-to-top', 38147 text: '', 38148 min: 60, 38149 fadeIn: 400, 38150 fadeOut: 400, 38151 scrollSpeed: 800, 38152 easingType: 'easeInOutExpo' 38153 } 38154 38155 $('body').append('<a href="#" id="' + settings.button.substring(1) + '" title="' + settings.text + '">' + settings.text + '</a>'); 38156 38157 $(settings.button).click(function (e) { 38158 $('html, body').animate({ scrollTop: 0 }, settings.scrollSpeed, settings.easingType); 38159 e.preventDefault(); 38160 }); 38161 38162 $(window).scroll(function () { 38163 var position = $(window).scrollTop(); 38164 38165 if (position > settings.min) { 38166 $(settings.button).addClass("active"); 38167 } 38168 else { 38169 $(settings.button).removeClass("active"); 38170 } 38171 }); 38172 38173})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.