diff options
| author | mo khan <mo@mokhan.ca> | 2013-07-16 09:11:49 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2013-07-16 09:11:49 -0600 |
| commit | b6cd251a543dbbfae926a8a403e66160aad84209 (patch) | |
| tree | 61c517415c591ea3680b6e110557eb1934ce0650 | |
drop in the ember starter kit
| -rwxr-xr-x | TODO.txt | 23 | ||||
| -rwxr-xr-x | css/normalize.css | 402 | ||||
| -rwxr-xr-x | css/style.css | 4 | ||||
| -rw-r--r-- | index.html | 31 | ||||
| -rwxr-xr-x | js/app.js | 11 | ||||
| -rwxr-xr-x | js/libs/ember-1.0.0-rc.6.js | 30970 | ||||
| -rwxr-xr-x | js/libs/handlebars-1.0.0-rc.4.js | 2239 | ||||
| -rwxr-xr-x | js/libs/jquery-1.9.1.js | 9597 |
8 files changed, 43277 insertions, 0 deletions
diff --git a/TODO.txt b/TODO.txt new file mode 100755 index 0000000..9de4860 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,23 @@ + ______ _ _ + | ____| | | (_) + | |__ _ __ ___ | |__ ___ _ ___ _ ___ + | __| | '_ ` _ \| '_ \ / _ \ '__/| |/ __| + | |____| | | | | | |_) | __/ | _ | |\__ \ + |______|_| |_| |_|_.__/ \___|_|(_)| ||___/ + _/ | + |__/ + +Your Ember.js project is almost ready! Here's how to get started: + +- Start writing your app in js/app.js. + +- Describe your application HTML in index.html. + +- During development, you can link to js/libs/ember-*.js to get the + unminified version of Ember.js. + +- Add CSS to css/style.css + +== Contact ======================= + +www.emberjs.com diff --git a/css/normalize.css b/css/normalize.css new file mode 100755 index 0000000..6d24a38 --- /dev/null +++ b/css/normalize.css @@ -0,0 +1,402 @@ +/*! normalize.css v2.1.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +/** + * Correct `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/** + * 1. Prevent system color scheme's background color being used in Firefox, IE, + * and Opera. + * 2. Prevent system color scheme's text color being used in Firefox, IE, and + * Opera. + * 3. Set default font family to sans-serif. + * 4. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + background: #fff; /* 1 */ + color: #000; /* 2 */ + font-family: sans-serif; /* 3 */ + -ms-text-size-adjust: 100%; /* 4 */ + -webkit-text-size-adjust: 100%; /* 4 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/** + * Address `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Correct font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/** + * Improve readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre-wrap; +} + +/** + * Set consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/** + * Correct overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Correct font family not being inherited in all browsers. + * 2. Correct font size not being inherited in all browsers. + * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. + * Correct `select` style inheritance in Firefox 4+ and Opera. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * 1. Address box sizing set to `content-box` in IE 8/9. + * 2. Remove excess padding in IE 8/9. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * 1. Remove default vertical scrollbar in IE 8/9. + * 2. Improve readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/css/style.css b/css/style.css new file mode 100755 index 0000000..b12e9a4 --- /dev/null +++ b/css/style.css @@ -0,0 +1,4 @@ +/* Put your CSS here */ +html, body { + margin: 20px; +}
\ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..08e7f70 --- /dev/null +++ b/index.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Ember Starter Kit</title> + <link rel="stylesheet" href="css/normalize.css"> + <link rel="stylesheet" href="css/style.css"> +</head> +<body> + + <script type="text/x-handlebars"> + <h2>Welcome to Ember.js</h2> + + {{outlet}} + </script> + + <script type="text/x-handlebars" data-template-name="index"> + <ul> + {{#each item in model}} + <li>{{item}}</li> + {{/each}} + </ul> + </script> + + <script src="js/libs/jquery-1.9.1.js"></script> + <script src="js/libs/handlebars-1.0.0-rc.4.js"></script> + <script src="js/libs/ember-1.0.0-rc.6.js"></script> + <script src="js/app.js"></script> + +</body> +</html> diff --git a/js/app.js b/js/app.js new file mode 100755 index 0000000..a88ce3e --- /dev/null +++ b/js/app.js @@ -0,0 +1,11 @@ +App = Ember.Application.create(); + +App.Router.map(function() { + // put your routes here +}); + +App.IndexRoute = Ember.Route.extend({ + model: function() { + return ['red', 'yellow', 'blue']; + } +}); diff --git a/js/libs/ember-1.0.0-rc.6.js b/js/libs/ember-1.0.0-rc.6.js new file mode 100755 index 0000000..0272084 --- /dev/null +++ b/js/libs/ember-1.0.0-rc.6.js @@ -0,0 +1,30970 @@ +// Version: v1.0.0-rc.5-113-g720acd8 +// Last commit: 720acd8 (2013-06-22 09:19:49 -0700) + + +(function() { +/*global __fail__*/ + +/** +Ember Debug + +@module ember +@submodule ember-debug +*/ + +/** +@class Ember +*/ + +if ('undefined' === typeof Ember) { + Ember = {}; + + if ('undefined' !== typeof window) { + window.Em = window.Ember = Em = Ember; + } +} + +Ember.ENV = 'undefined' === typeof ENV ? {} : ENV; + +if (!('MANDATORY_SETTER' in Ember.ENV)) { + Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist +} + +/** + Define an assertion that will throw an exception if the condition is not + met. Ember build tools will remove any calls to `Ember.assert()` when + doing a production build. Example: + + ```javascript + // Test for truthiness + Ember.assert('Must pass a valid object', obj); + // Fail unconditionally + Ember.assert('This code path should never be run') + ``` + + @method assert + @param {String} desc A description of the assertion. This will become + the text of the Error thrown if the assertion fails. + @param {Boolean} test Must be truthy for the assertion to pass. If + falsy, an exception will be thrown. +*/ +Ember.assert = function(desc, test) { + Ember.Logger.assert(test, desc); + + if (Ember.testing && !test) { + // when testing, ensure test failures when assertions fail + throw new Error("Assertion Failed: " + desc); + } +}; + + +/** + Display a warning with the provided message. Ember build tools will + remove any calls to `Ember.warn()` when doing a production build. + + @method warn + @param {String} message A warning to display. + @param {Boolean} test An optional boolean. If falsy, the warning + will be displayed. +*/ +Ember.warn = function(message, test) { + if (!test) { + Ember.Logger.warn("WARNING: "+message); + if ('trace' in Ember.Logger) Ember.Logger.trace(); + } +}; + +/** + Display a debug notice. Ember build tools will remove any calls to + `Ember.debug()` when doing a production build. + + ```javascript + Ember.debug("I'm a debug notice!"); + ``` + + @method debug + @param {String} message A debug message to display. +*/ +Ember.debug = function(message) { + Ember.Logger.debug("DEBUG: "+message); +}; + +/** + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only). Ember build tools will remove any calls to + `Ember.deprecate()` when doing a production build. + + @method deprecate + @param {String} message A description of the deprecation. + @param {Boolean} test An optional boolean. If falsy, the deprecation + will be displayed. +*/ +Ember.deprecate = function(message, test) { + if (Ember.TESTING_DEPRECATION) { return; } + + if (arguments.length === 1) { test = false; } + if (test) { return; } + + if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new Error(message); } + + var error; + + // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome + try { __fail__.fail(); } catch (e) { error = e; } + + if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { + var stack, stackStr = ''; + if (error['arguments']) { + // Chrome + stack = error.stack.replace(/^\s+at\s+/gm, ''). + replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2'). + replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); + stack.shift(); + } else { + // Firefox + stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). + replace(/^\(/gm, '{anonymous}(').split('\n'); + } + + stackStr = "\n " + stack.slice(2).join("\n "); + message = message + stackStr; + } + + Ember.Logger.warn("DEPRECATION: "+message); +}; + + + +/** + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only) when the wrapped method is called. + + Ember build tools will not remove calls to `Ember.deprecateFunc()`, though + no warnings will be shown in production. + + @method deprecateFunc + @param {String} message A description of the deprecation. + @param {Function} func The function to be deprecated. + @return {Function} a new function that wrapped the original function with a deprecation warning +*/ +Ember.deprecateFunc = function(message, func) { + return function() { + Ember.deprecate(message); + return func.apply(this, arguments); + }; +}; + +})(); + +// Version: v1.0.0-rc.5-113-g720acd8 +// Last commit: 720acd8 (2013-06-22 09:19:49 -0700) + + +(function() { +var define, requireModule; + +(function() { + var registry = {}, seen = {}; + + define = function(name, deps, callback) { + registry[name] = { deps: deps, callback: callback }; + }; + + requireModule = function(name) { + if (seen[name]) { return seen[name]; } + seen[name] = {}; + + var mod, deps, callback, reified, exports; + + mod = registry[name]; + + if (!mod) { + throw new Error("Module '" + name + "' not found."); + } + + deps = mod.deps; + callback = mod.callback; + reified = []; + exports; + + for (var i=0, l=deps.length; i<l; i++) { + if (deps[i] === 'exports') { + reified.push(exports = {}); + } else { + reified.push(requireModule(deps[i])); + } + } + + var value = callback.apply(this, reified); + return seen[name] = exports || value; + }; +})(); +(function() { +/*globals Em:true ENV */ + +/** +@module ember +@submodule ember-metal +*/ + +/** + All Ember methods and functions are defined inside of this namespace. You + generally should not add new properties to this namespace as it may be + overwritten by future versions of Ember. + + You can also use the shorthand `Em` instead of `Ember`. + + Ember-Runtime is a framework that provides core functions for Ember including + cross-platform functions, support for property observing and objects. Its + focus is on small size and performance. You can use this in place of or + along-side other cross-platform libraries such as jQuery. + + The core Runtime framework is based on the jQuery API with a number of + performance optimizations. + + @class Ember + @static + @version 1.0.0-rc.6 +*/ + +if ('undefined' === typeof Ember) { + // Create core object. Make it act like an instance of Ember.Namespace so that + // objects assigned to it are given a sane string representation. + Ember = {}; +} + +// Default imports, exports and lookup to the global object; +var imports = Ember.imports = Ember.imports || this; +var exports = Ember.exports = Ember.exports || this; +var lookup = Ember.lookup = Ember.lookup || this; + +// aliases needed to keep minifiers from removing the global context +exports.Em = exports.Ember = Em = Ember; + +// Make sure these are set whether Ember was already defined or not + +Ember.isNamespace = true; + +Ember.toString = function() { return "Ember"; }; + + +/** + @property VERSION + @type String + @default '1.0.0-rc.6' + @final +*/ +Ember.VERSION = '1.0.0-rc.6'; + +/** + Standard environmental variables. You can define these in a global `ENV` + variable before loading Ember to control various configuration + settings. + + @property ENV + @type Hash +*/ +Ember.ENV = Ember.ENV || ('undefined' === typeof ENV ? {} : ENV); + +Ember.config = Ember.config || {}; + +// .......................................................... +// BOOTSTRAP +// + +/** + Determines whether Ember should enhances some built-in object prototypes to + provide a more friendly API. If enabled, a few methods will be added to + `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced, + which is the one that causes most trouble for people. + + In general we recommend leaving this option set to true since it rarely + conflicts with other code. If you need to turn it off however, you can + define an `ENV.EXTEND_PROTOTYPES` config to disable it. + + @property EXTEND_PROTOTYPES + @type Boolean + @default true +*/ +Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; + +if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { + Ember.EXTEND_PROTOTYPES = true; +} + +/** + Determines whether Ember logs a full stack trace during deprecation warnings + + @property LOG_STACKTRACE_ON_DEPRECATION + @type Boolean + @default true +*/ +Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); + +/** + Determines whether Ember should add ECMAScript 5 shims to older browsers. + + @property SHIM_ES5 + @type Boolean + @default Ember.EXTEND_PROTOTYPES +*/ +Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; + +/** + Determines whether Ember logs info about version of used libraries + + @property LOG_VERSION + @type Boolean + @default true +*/ +Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; + +/** + Empty function. Useful for some operations. + + @method K + @private + @return {Object} +*/ +Ember.K = function() { return this; }; + + +// Stub out the methods defined by the ember-debug package in case it's not loaded + +if ('undefined' === typeof Ember.assert) { Ember.assert = Ember.K; } +if ('undefined' === typeof Ember.warn) { Ember.warn = Ember.K; } +if ('undefined' === typeof Ember.debug) { Ember.debug = Ember.K; } +if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = Ember.K; } +if ('undefined' === typeof Ember.deprecateFunc) { + Ember.deprecateFunc = function(_, func) { return func; }; +} + +/** + Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from + jQuery master. We'll just bootstrap our own uuid now. + + @property uuid + @type Number + @private +*/ +Ember.uuid = 0; + +// .......................................................... +// LOGGER +// + +function consoleMethod(name) { + if (imports.console && imports.console[name]) { + // Older IE doesn't support apply, but Chrome needs it + if (imports.console[name].apply) { + return function() { + imports.console[name].apply(imports.console, arguments); + }; + } else { + return function() { + var message = Array.prototype.join.call(arguments, ', '); + imports.console[name](message); + }; + } + } +} + +function assertPolyfill(test, message) { + if (!test) { + try { + // attempt to preserve the stack + throw new Error("assertion failed: " + message); + } catch(error) { + setTimeout(function(){ + throw error; + }, 0); + } + } +} + +/** + Inside Ember-Metal, simply uses the methods from `imports.console`. + Override this to provide more robust logging functionality. + + @class Logger + @namespace Ember +*/ +Ember.Logger = { + log: consoleMethod('log') || Ember.K, + warn: consoleMethod('warn') || Ember.K, + error: consoleMethod('error') || Ember.K, + info: consoleMethod('info') || Ember.K, + debug: consoleMethod('debug') || consoleMethod('info') || Ember.K, + assert: consoleMethod('assert') || assertPolyfill +}; + + +// .......................................................... +// ERROR HANDLING +// + +/** + A function may be assigned to `Ember.onerror` to be called when Ember + internals encounter an error. This is useful for specialized error handling + and reporting code. + + @event onerror + @for Ember + @param {Exception} error the error object +*/ +Ember.onerror = null; + +/** + @private + + Wrap code block in a try/catch if `Ember.onerror` is set. + + @method handleErrors + @for Ember + @param {Function} func + @param [context] +*/ +Ember.handleErrors = function(func, context) { + // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error, + // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch + if ('function' === typeof Ember.onerror) { + try { + return func.call(context || this); + } catch (error) { + Ember.onerror(error); + } + } else { + return func.call(context || this); + } +}; + +Ember.merge = function(original, updates) { + for (var prop in updates) { + if (!updates.hasOwnProperty(prop)) { continue; } + original[prop] = updates[prop]; + } + return original; +}; + +/** + Returns true if the passed value is null or undefined. This avoids errors + from JSLint complaining about use of ==, which can be technically + confusing. + + ```javascript + Ember.isNone(); // true + Ember.isNone(null); // true + Ember.isNone(undefined); // true + Ember.isNone(''); // false + Ember.isNone([]); // false + Ember.isNone(function(){}); // false + ``` + + @method isNone + @for Ember + @param {Object} obj Value to test + @return {Boolean} +*/ +Ember.isNone = function(obj) { + return obj === null || obj === undefined; +}; +Ember.none = Ember.deprecateFunc("Ember.none is deprecated. Please use Ember.isNone instead.", Ember.isNone); + +/** + Verifies that a value is `null` or an empty string, empty array, + or empty function. + + Constrains the rules on `Ember.isNone` by returning false for empty + string and empty arrays. + + ```javascript + Ember.isEmpty(); // true + Ember.isEmpty(null); // true + Ember.isEmpty(undefined); // true + Ember.isEmpty(''); // true + Ember.isEmpty([]); // true + Ember.isEmpty('Adam Hawkins'); // false + Ember.isEmpty([0,1,2]); // false + ``` + + @method isEmpty + @for Ember + @param {Object} obj Value to test + @return {Boolean} +*/ +Ember.isEmpty = function(obj) { + return Ember.isNone(obj) || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && Ember.get(obj, 'length') === 0); +}; +Ember.empty = Ember.deprecateFunc("Ember.empty is deprecated. Please use Ember.isEmpty instead.", Ember.isEmpty) ; + + +})(); + + + +(function() { +/*globals Node */ +/** +@module ember-metal +*/ + +/** + Platform specific methods and feature detectors needed by the framework. + + @class platform + @namespace Ember + @static +*/ +var platform = Ember.platform = {}; + + +/** + Identical to `Object.create()`. Implements if not available natively. + + @method create + @for Ember +*/ +Ember.create = Object.create; + +// IE8 has Object.create but it couldn't treat property descripters. +if (Ember.create) { + if (Ember.create({a: 1}, {a: {value: 2}}).a !== 2) { + Ember.create = null; + } +} + +// STUB_OBJECT_CREATE allows us to override other libraries that stub +// Object.create different than we would prefer +if (!Ember.create || Ember.ENV.STUB_OBJECT_CREATE) { + var K = function() {}; + + Ember.create = function(obj, props) { + K.prototype = obj; + obj = new K(); + if (props) { + K.prototype = obj; + for (var prop in props) { + K.prototype[prop] = props[prop].value; + } + obj = new K(); + } + K.prototype = null; + + return obj; + }; + + Ember.create.isSimulated = true; +} + +var defineProperty = Object.defineProperty; +var canRedefineProperties, canDefinePropertyOnDOM; + +// Catch IE8 where Object.defineProperty exists but only works on DOM elements +if (defineProperty) { + try { + defineProperty({}, 'a',{get:function(){}}); + } catch (e) { + defineProperty = null; + } +} + +if (defineProperty) { + // Detects a bug in Android <3.2 where you cannot redefine a property using + // Object.defineProperty once accessors have already been set. + canRedefineProperties = (function() { + var obj = {}; + + defineProperty(obj, 'a', { + configurable: true, + enumerable: true, + get: function() { }, + set: function() { } + }); + + defineProperty(obj, 'a', { + configurable: true, + enumerable: true, + writable: true, + value: true + }); + + return obj.a === true; + })(); + + // This is for Safari 5.0, which supports Object.defineProperty, but not + // on DOM nodes. + canDefinePropertyOnDOM = (function(){ + try { + defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); + return true; + } catch(e) { } + + return false; + })(); + + if (!canRedefineProperties) { + defineProperty = null; + } else if (!canDefinePropertyOnDOM) { + defineProperty = function(obj, keyName, desc){ + var isNode; + + if (typeof Node === "object") { + isNode = obj instanceof Node; + } else { + isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; + } + + if (isNode) { + // TODO: Should we have a warning here? + return (obj[keyName] = desc.value); + } else { + return Object.defineProperty(obj, keyName, desc); + } + }; + } +} + +/** +@class platform +@namespace Ember +*/ + +/** + Identical to `Object.defineProperty()`. Implements as much functionality + as possible if not available natively. + + @method defineProperty + @param {Object} obj The object to modify + @param {String} keyName property name to modify + @param {Object} desc descriptor hash + @return {void} +*/ +platform.defineProperty = defineProperty; + +/** + Set to true if the platform supports native getters and setters. + + @property hasPropertyAccessors + @final +*/ +platform.hasPropertyAccessors = true; + +if (!platform.defineProperty) { + platform.hasPropertyAccessors = false; + + platform.defineProperty = function(obj, keyName, desc) { + if (!desc.get) { obj[keyName] = desc.value; } + }; + + platform.defineProperty.isSimulated = true; +} + +if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) { + Ember.ENV.MANDATORY_SETTER = false; +} + +})(); + + + +(function() { +/*jshint newcap:false*/ +/** +@module ember-metal +*/ + +// NOTE: There is a bug in jshint that doesn't recognize `Object()` without `new` +// as being ok unless both `newcap:false` and not `use strict`. +// https://github.com/jshint/jshint/issues/392 + +// Testing this is not ideal, but we want to use native functions +// if available, but not to use versions created by libraries like Prototype +var isNativeFunc = function(func) { + // This should probably work in all browsers likely to have ES5 array methods + return func && Function.prototype.toString.call(func).indexOf('[native code]') > -1; +}; + +// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map +var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : function(fun /*, thisp */) { + //"use strict"; + + if (this === void 0 || this === null) { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") { + throw new TypeError(); + } + + var res = new Array(len); + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + res[i] = fun.call(thisp, t[i], i, t); + } + } + + return res; +}; + +// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach +var arrayForEach = isNativeFunc(Array.prototype.forEach) ? Array.prototype.forEach : function(fun /*, thisp */) { + //"use strict"; + + if (this === void 0 || this === null) { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== "function") { + throw new TypeError(); + } + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + fun.call(thisp, t[i], i, t); + } + } +}; + +var arrayIndexOf = isNativeFunc(Array.prototype.indexOf) ? Array.prototype.indexOf : function (obj, fromIndex) { + if (fromIndex === null || fromIndex === undefined) { fromIndex = 0; } + else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); } + for (var i = fromIndex, j = this.length; i < j; i++) { + if (this[i] === obj) { return i; } + } + return -1; +}; + +Ember.ArrayPolyfills = { + map: arrayMap, + forEach: arrayForEach, + indexOf: arrayIndexOf +}; + +if (Ember.SHIM_ES5) { + if (!Array.prototype.map) { + Array.prototype.map = arrayMap; + } + + if (!Array.prototype.forEach) { + Array.prototype.forEach = arrayForEach; + } + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = arrayIndexOf; + } +} + +})(); + + + +(function() { +/** +@module ember-metal +*/ + + +var o_defineProperty = Ember.platform.defineProperty, + o_create = Ember.create, + // Used for guid generation... + GUID_KEY = '__ember'+ (+ new Date()), + uuid = 0, + numberCache = [], + stringCache = {}; + +var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + +/** + @private + + A unique key used to assign guids and other private metadata to objects. + If you inspect an object in your browser debugger you will often see these. + They can be safely ignored. + + On browsers that support it, these properties are added with enumeration + disabled so they won't show up when you iterate over your properties. + + @property GUID_KEY + @for Ember + @type String + @final +*/ +Ember.GUID_KEY = GUID_KEY; + +var GUID_DESC = { + writable: false, + configurable: false, + enumerable: false, + value: null +}; + +/** + @private + + Generates a new guid, optionally saving the guid to the object that you + pass in. You will rarely need to use this method. Instead you should + call `Ember.guidFor(obj)`, which return an existing guid if available. + + @method generateGuid + @for Ember + @param {Object} [obj] Object the guid will be used for. If passed in, the guid will + be saved on the object and reused whenever you pass the same object + again. + + If no object is passed, just generate a new guid. + @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to + separate the guid into separate namespaces. + @return {String} the guid +*/ +Ember.generateGuid = function generateGuid(obj, prefix) { + if (!prefix) prefix = 'ember'; + var ret = (prefix + (uuid++)); + if (obj) { + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + } + return ret ; +}; + +/** + @private + + Returns a unique id for the object. If the object does not yet have a guid, + one will be assigned to it. You can call this on any object, + `Ember.Object`-based or not, but be aware that it will add a `_guid` + property. + + You can also use this method on DOM Element objects. + + @method guidFor + @for Ember + @param {Object} obj any object, string, number, Element, or primitive + @return {String} the unique guid for this instance. +*/ +Ember.guidFor = function guidFor(obj) { + + // special cases where we don't want to add a key to object + if (obj === undefined) return "(undefined)"; + if (obj === null) return "(null)"; + + var ret; + var type = typeof obj; + + // Don't allow prototype changes to String etc. to change the guidFor + switch(type) { + case 'number': + ret = numberCache[obj]; + if (!ret) ret = numberCache[obj] = 'nu'+obj; + return ret; + + case 'string': + ret = stringCache[obj]; + if (!ret) ret = stringCache[obj] = 'st'+(uuid++); + return ret; + + case 'boolean': + return obj ? '(true)' : '(false)'; + + default: + if (obj[GUID_KEY]) return obj[GUID_KEY]; + if (obj === Object) return '(Object)'; + if (obj === Array) return '(Array)'; + ret = 'ember'+(uuid++); + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + return ret; + } +}; + +// .......................................................... +// META +// + +var META_DESC = { + writable: true, + configurable: false, + enumerable: false, + value: null +}; + +var META_KEY = Ember.GUID_KEY+'_meta'; + +/** + The key used to store meta information on object for property observing. + + @property META_KEY + @for Ember + @private + @final + @type String +*/ +Ember.META_KEY = META_KEY; + +// Placeholder for non-writable metas. +var EMPTY_META = { + descs: {}, + watching: {} +}; + +if (MANDATORY_SETTER) { EMPTY_META.values = {}; } + +Ember.EMPTY_META = EMPTY_META; + +if (Object.freeze) Object.freeze(EMPTY_META); + +var isDefinePropertySimulated = Ember.platform.defineProperty.isSimulated; + +function Meta(obj) { + this.descs = {}; + this.watching = {}; + this.cache = {}; + this.source = obj; +} + +if (isDefinePropertySimulated) { + // on platforms that don't support enumerable false + // make meta fail jQuery.isPlainObject() to hide from + // jQuery.extend() by having a property that fails + // hasOwnProperty check. + Meta.prototype.__preventPlainObject__ = true; + + // Without non-enumerable properties, meta objects will be output in JSON + // unless explicitly suppressed + Meta.prototype.toJSON = function () { }; +} + +/** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. + + The meta object contains information about computed property descriptors as + well as any watched properties and other information. You generally will + not access this information directly but instead work with higher level + methods that manipulate this hash indirectly. + + @method meta + @for Ember + @private + + @param {Object} obj The object to retrieve meta for + @param {Boolean} [writable=true] Pass `false` if you do not intend to modify + the meta hash, allowing the method to avoid making an unnecessary copy. + @return {Object} the meta hash for an object +*/ +Ember.meta = function meta(obj, writable) { + + var ret = obj[META_KEY]; + if (writable===false) return ret || EMPTY_META; + + if (!ret) { + if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); + + ret = new Meta(obj); + + if (MANDATORY_SETTER) { ret.values = {}; } + + obj[META_KEY] = ret; + + // make sure we don't accidentally try to create constructor like desc + ret.descs.constructor = null; + + } else if (ret.source !== obj) { + if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); + + ret = o_create(ret); + ret.descs = o_create(ret.descs); + ret.watching = o_create(ret.watching); + ret.cache = {}; + ret.source = obj; + + if (MANDATORY_SETTER) { ret.values = o_create(ret.values); } + + obj[META_KEY] = ret; + } + return ret; +}; + +Ember.getMeta = function getMeta(obj, property) { + var meta = Ember.meta(obj, false); + return meta[property]; +}; + +Ember.setMeta = function setMeta(obj, property, value) { + var meta = Ember.meta(obj, true); + meta[property] = value; + return value; +}; + +/** + @deprecated + @private + + In order to store defaults for a class, a prototype may need to create + a default meta object, which will be inherited by any objects instantiated + from the class's constructor. + + However, the properties of that meta object are only shallow-cloned, + so if a property is a hash (like the event system's `listeners` hash), + it will by default be shared across all instances of that class. + + This method allows extensions to deeply clone a series of nested hashes or + other complex objects. For instance, the event system might pass + `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will + walk down the keys provided. + + For each key, if the key does not exist, it is created. If it already + exists and it was inherited from its constructor, the constructor's + key is cloned. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @method metaPath + @for Ember + @param {Object} obj The object whose meta we are examining + @param {Array} path An array of keys to walk down + @param {Boolean} writable whether or not to create a new meta + (or meta property) if one does not already exist or if it's + shared with its constructor +*/ +Ember.metaPath = function metaPath(obj, path, writable) { + Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); + var meta = Ember.meta(obj, writable), keyName, value; + + for (var i=0, l=path.length; i<l; i++) { + keyName = path[i]; + value = meta[keyName]; + + if (!value) { + if (!writable) { return undefined; } + value = meta[keyName] = { __ember_source__: obj }; + } else if (value.__ember_source__ !== obj) { + if (!writable) { return undefined; } + value = meta[keyName] = o_create(value); + value.__ember_source__ = obj; + } + + meta = value; + } + + return value; +}; + +/** + @private + + Wraps the passed function so that `this._super` will point to the superFunc + when the function is invoked. This is the primitive we use to implement + calls to super. + + @method wrap + @for Ember + @param {Function} func The function to call + @param {Function} superFunc The super function. + @return {Function} wrapped function. +*/ +Ember.wrap = function(func, superFunc) { + function K() {} + + function superWrapper() { + var ret, sup = this._super; + this._super = superFunc || K; + ret = func.apply(this, arguments); + this._super = sup; + return ret; + } + + superWrapper.wrappedFunction = func; + superWrapper.__ember_observes__ = func.__ember_observes__; + superWrapper.__ember_observesBefore__ = func.__ember_observesBefore__; + + return superWrapper; +}; + +/** + Returns true if the passed object is an array or Array-like. + + Ember Array Protocol: + + - the object has an objectAt property + - the object is a native Array + - the object is an Object, and has a length property + + Unlike `Ember.typeOf` this method returns true even if the passed object is + not formally array but appears to be array-like (i.e. implements `Ember.Array`) + + ```javascript + Ember.isArray(); // false + Ember.isArray([]); // true + Ember.isArray( Ember.ArrayProxy.create({ content: [] }) ); // true + ``` + + @method isArray + @for Ember + @param {Object} obj The object to test + @return {Boolean} true if the passed object is an array or Array-like +*/ +Ember.isArray = function(obj) { + if (!obj || obj.setInterval) { return false; } + if (Array.isArray && Array.isArray(obj)) { return true; } + if (Ember.Array && Ember.Array.detect(obj)) { return true; } + if ((obj.length !== undefined) && 'object'===typeof obj) { return true; } + return false; +}; + +/** + Forces the passed object to be part of an array. If the object is already + an array or array-like, returns the object. Otherwise adds the object to + an array. If obj is `null` or `undefined`, returns an empty array. + + ```javascript + Ember.makeArray(); // [] + Ember.makeArray(null); // [] + Ember.makeArray(undefined); // [] + Ember.makeArray('lindsay'); // ['lindsay'] + Ember.makeArray([1,2,42]); // [1,2,42] + + var controller = Ember.ArrayProxy.create({ content: [] }); + Ember.makeArray(controller) === controller; // true + ``` + + @method makeArray + @for Ember + @param {Object} obj the object + @return {Array} +*/ +Ember.makeArray = function(obj) { + if (obj === null || obj === undefined) { return []; } + return Ember.isArray(obj) ? obj : [obj]; +}; + +function canInvoke(obj, methodName) { + return !!(obj && typeof obj[methodName] === 'function'); +} + +/** + Checks to see if the `methodName` exists on the `obj`. + + @method canInvoke + @for Ember + @param {Object} obj The object to check for the method + @param {String} methodName The method name to check for +*/ +Ember.canInvoke = canInvoke; + +/** + Checks to see if the `methodName` exists on the `obj`, + and if it does, invokes it with the arguments passed. + + @method tryInvoke + @for Ember + @param {Object} obj The object to check for the method + @param {String} methodName The method name to check for + @param {Array} [args] The arguments to pass to the method + @return {*} the return value of the invoked method or undefined if it cannot be invoked +*/ +Ember.tryInvoke = function(obj, methodName, args) { + if (canInvoke(obj, methodName)) { + return obj[methodName].apply(obj, args || []); + } +}; + +// https://github.com/emberjs/ember.js/pull/1617 +var needsFinallyFix = (function() { + var count = 0; + try{ + try { } + finally { + count++; + throw new Error('needsFinallyFixTest'); + } + } catch (e) {} + + return count !== 1; +})(); + +/** + Provides try { } finally { } functionality, while working + around Safari's double finally bug. + + @method tryFinally + @for Ember + @param {Function} tryable The function to run the try callback + @param {Function} finalizer The function to run the finally callback + @param [binding] + @return {*} The return value is the that of the finalizer, + unless that valueis undefined, in which case it is the return value + of the tryable +*/ + +if (needsFinallyFix) { + Ember.tryFinally = function(tryable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e){ + finalError = e; + } + } + + if (finalError) { throw finalError; } + + return (finalResult === undefined) ? result : finalResult; + }; +} else { + Ember.tryFinally = function(tryable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + finalResult = finalizer.call(binding); + } + + return (finalResult === undefined) ? result : finalResult; + }; +} + +/** + Provides try { } catch finally { } functionality, while working + around Safari's double finally bug. + + @method tryCatchFinally + @for Ember + @param {Function} tryable The function to run the try callback + @param {Function} catchable The function to run the catchable callback + @param {Function} finalizer The function to run the finally callback + @param [binding] + @return {*} The return value is the that of the finalizer, + unless that value is undefined, in which case it is the return value + of the tryable. +*/ +if (needsFinallyFix) { + Ember.tryCatchFinally = function(tryable, catchable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e){ + finalError = e; + } + } + + if (finalError) { throw finalError; } + + return (finalResult === undefined) ? result : finalResult; + }; +} else { + Ember.tryCatchFinally = function(tryable, catchable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + finalResult = finalizer.call(binding); + } + + return (finalResult === undefined) ? result : finalResult; + }; +} + +// ........................................ +// TYPING & ARRAY MESSAGING +// + +var TYPE_MAP = {}; +var t = "Boolean Number String Function Array Date RegExp Object".split(" "); +Ember.ArrayPolyfills.forEach.call(t, function(name) { + TYPE_MAP[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +var toString = Object.prototype.toString; + +/** + Returns a consistent type for the passed item. + + Use this instead of the built-in `typeof` to get the type of an item. + It will return the same result across all browsers and includes a bit + more detail. Here is what will be returned: + + | Return Value | Meaning | + |---------------|------------------------------------------------------| + | 'string' | String primitive | + | 'number' | Number primitive | + | 'boolean' | Boolean primitive | + | 'null' | Null value | + | 'undefined' | Undefined value | + | 'function' | A function | + | 'array' | An instance of Array | + | 'class' | An Ember class (created using Ember.Object.extend()) | + | 'instance' | An Ember object instance | + | 'error' | An instance of the Error object | + | 'object' | A JavaScript object not inheriting from Ember.Object | + + Examples: + + ```javascript + Ember.typeOf(); // 'undefined' + Ember.typeOf(null); // 'null' + Ember.typeOf(undefined); // 'undefined' + Ember.typeOf('michael'); // 'string' + Ember.typeOf(101); // 'number' + Ember.typeOf(true); // 'boolean' + Ember.typeOf(Ember.makeArray); // 'function' + Ember.typeOf([1,2,90]); // 'array' + Ember.typeOf(Ember.Object.extend()); // 'class' + Ember.typeOf(Ember.Object.create()); // 'instance' + Ember.typeOf(new Error('teamocil')); // 'error' + + // "normal" JavaScript object + Ember.typeOf({a: 'b'}); // 'object' + ``` + + @method typeOf + @for Ember + @param {Object} item the item to check + @return {String} the type +*/ +Ember.typeOf = function(item) { + var ret; + + ret = (item === null || item === undefined) ? String(item) : TYPE_MAP[toString.call(item)] || 'object'; + + if (ret === 'function') { + if (Ember.Object && Ember.Object.detect(item)) ret = 'class'; + } else if (ret === 'object') { + if (item instanceof Error) ret = 'error'; + else if (Ember.Object && item instanceof Ember.Object) ret = 'instance'; + else ret = 'object'; + } + + return ret; +}; + +})(); + + + +(function() { +// Ember.tryCatchFinally + +/** + The purpose of the Ember Instrumentation module is + to provide efficient, general-purpose instrumentation + for Ember. + + Subscribe to a listener by using `Ember.subscribe`: + + ```javascript + Ember.subscribe("render", { + before: function(name, timestamp, payload) { + + }, + + after: function(name, timestamp, payload) { + + } + }); + ``` + + If you return a value from the `before` callback, that same + value will be passed as a fourth parameter to the `after` + callback. + + Instrument a block of code by using `Ember.instrument`: + + ```javascript + Ember.instrument("render.handlebars", payload, function() { + // rendering logic + }, binding); + ``` + + Event names passed to `Ember.instrument` are namespaced + by periods, from more general to more specific. Subscribers + can listen for events by whatever level of granularity they + are interested in. + + In the above example, the event is `render.handlebars`, + and the subscriber listened for all events beginning with + `render`. It would receive callbacks for events named + `render`, `render.handlebars`, `render.container`, or + even `render.handlebars.layout`. + + @class Instrumentation + @namespace Ember + @static +*/ +Ember.Instrumentation = {}; + +var subscribers = [], cache = {}; + +var populateListeners = function(name) { + var listeners = [], subscriber; + + for (var i=0, l=subscribers.length; i<l; i++) { + subscriber = subscribers[i]; + if (subscriber.regex.test(name)) { + listeners.push(subscriber.object); + } + } + + cache[name] = listeners; + return listeners; +}; + +var time = (function() { + var perf = 'undefined' !== typeof window ? window.performance || {} : {}; + var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow; + // fn.bind will be available in all the browsers that support the advanced window.performance... ;-) + return fn ? fn.bind(perf) : function() { return +new Date(); }; +})(); + + +Ember.Instrumentation.instrument = function(name, payload, callback, binding) { + var listeners = cache[name], timeName, ret; + + if (Ember.STRUCTURED_PROFILE) { + timeName = name + ": " + payload.object; + console.time(timeName); + } + + if (!listeners) { + listeners = populateListeners(name); + } + + if (listeners.length === 0) { + ret = callback.call(binding); + if (Ember.STRUCTURED_PROFILE) { console.timeEnd(timeName); } + return ret; + } + + var beforeValues = [], listener, i, l; + + function tryable(){ + for (i=0, l=listeners.length; i<l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, time(), payload); + } + + return callback.call(binding); + } + + function catchable(e){ + payload = payload || {}; + payload.exception = e; + } + + function finalizer() { + for (i=0, l=listeners.length; i<l; i++) { + listener = listeners[i]; + listener.after(name, time(), payload, beforeValues[i]); + } + + if (Ember.STRUCTURED_PROFILE) { + console.timeEnd(timeName); + } + } + + return Ember.tryCatchFinally(tryable, catchable, finalizer); +}; + +Ember.Instrumentation.subscribe = function(pattern, object) { + var paths = pattern.split("."), path, regex = []; + + for (var i=0, l=paths.length; i<l; i++) { + path = paths[i]; + if (path === "*") { + regex.push("[^\\.]*"); + } else { + regex.push(path); + } + } + + regex = regex.join("\\."); + regex = regex + "(\\..*)?"; + + var subscriber = { + pattern: pattern, + regex: new RegExp("^" + regex + "$"), + object: object + }; + + subscribers.push(subscriber); + cache = {}; + + return subscriber; +}; + +Ember.Instrumentation.unsubscribe = function(subscriber) { + var index; + + for (var i=0, l=subscribers.length; i<l; i++) { + if (subscribers[i] === subscriber) { + index = i; + } + } + + subscribers.splice(index, 1); + cache = {}; +}; + +Ember.Instrumentation.reset = function() { + subscribers = []; + cache = {}; +}; + +Ember.instrument = Ember.Instrumentation.instrument; +Ember.subscribe = Ember.Instrumentation.subscribe; + +})(); + + + +(function() { +var map, forEach, indexOf, concat; +concat = Array.prototype.concat; +map = Array.prototype.map || Ember.ArrayPolyfills.map; +forEach = Array.prototype.forEach || Ember.ArrayPolyfills.forEach; +indexOf = Array.prototype.indexOf || Ember.ArrayPolyfills.indexOf; + +var utils = Ember.EnumerableUtils = { + map: function(obj, callback, thisArg) { + return obj.map ? obj.map.call(obj, callback, thisArg) : map.call(obj, callback, thisArg); + }, + + forEach: function(obj, callback, thisArg) { + return obj.forEach ? obj.forEach.call(obj, callback, thisArg) : forEach.call(obj, callback, thisArg); + }, + + indexOf: function(obj, element, index) { + return obj.indexOf ? obj.indexOf.call(obj, element, index) : indexOf.call(obj, element, index); + }, + + indexesOf: function(obj, elements) { + return elements === undefined ? [] : utils.map(elements, function(item) { + return utils.indexOf(obj, item); + }); + }, + + addObject: function(array, item) { + var index = utils.indexOf(array, item); + if (index === -1) { array.push(item); } + }, + + removeObject: function(array, item) { + var index = utils.indexOf(array, item); + if (index !== -1) { array.splice(index, 1); } + }, + + replace: function(array, idx, amt, objects) { + if (array.replace) { + return array.replace(idx, amt, objects); + } else { + var args = concat.apply([idx, amt], objects); + return array.splice.apply(array, args); + } + }, + + intersection: function(array1, array2) { + var intersection = []; + + utils.forEach(array1, function(element) { + if (utils.indexOf(array2, element) >= 0) { + intersection.push(element); + } + }); + + return intersection; + } +}; + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +var META_KEY = Ember.META_KEY, get; + +var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + +var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/; +var HAS_THIS = /^this[\.\*]/; +var FIRST_KEY = /^([^\.\*]+)/; + +// .......................................................... +// GET AND SET +// +// If we are on a platform that supports accessors we can use those. +// Otherwise simulate accessors by looking up the property directly on the +// object. + +/** + Gets the value of a property on an object. If the property is computed, + the function will be invoked. If the property is not defined but the + object implements the `unknownProperty` method then that will be invoked. + + If you plan to run on IE8 and older browsers then you should use this + method anytime you want to retrieve a property on an object that you don't + know for sure is private. (Properties beginning with an underscore '_' + are considered private.) + + On all newer browsers, you only need to use this method to retrieve + properties if the property might not be defined on the object and you want + to respect the `unknownProperty` handler. Otherwise you can ignore this + method. + + Note that if the object itself is `undefined`, this method will throw + an error. + + @method get + @for Ember + @param {Object} obj The object to retrieve from. + @param {String} keyName The property key to retrieve + @return {Object} the property value or `null`. +*/ +get = function get(obj, keyName) { + // Helpers that operate with 'this' within an #each + if (keyName === '') { + return obj; + } + + if (!keyName && 'string'===typeof obj) { + keyName = obj; + obj = null; + } + + Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName); + Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); + + if (obj === null || keyName.indexOf('.') !== -1) { + return getPath(obj, keyName); + } + + var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; + if (desc) { + return desc.get(obj, keyName); + } else { + if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { + ret = meta.values[keyName]; + } else { + ret = obj[keyName]; + } + + if (ret === undefined && + 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { + return obj.unknownProperty(keyName); + } + + return ret; + } +}; + +// Currently used only by Ember Data tests +if (Ember.config.overrideAccessors) { + Ember.get = get; + Ember.config.overrideAccessors(); + get = Ember.get; +} + +/** + @private + + Normalizes a target/path pair to reflect that actual target/path that should + be observed, etc. This takes into account passing in global property + paths (i.e. a path beginning with a captial letter not defined on the + target) and * separators. + + @method normalizeTuple + @for Ember + @param {Object} target The current target. May be `null`. + @param {String} path A path on the target or a global property path. + @return {Array} a temporary array with the normalized target/path pair. +*/ +var normalizeTuple = Ember.normalizeTuple = function(target, path) { + var hasThis = HAS_THIS.test(path), + isGlobal = !hasThis && IS_GLOBAL_PATH.test(path), + key; + + if (!target || isGlobal) target = Ember.lookup; + if (hasThis) path = path.slice(5); + + if (target === Ember.lookup) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length+1); + } + + // must return some kind of path to be valid else other things will break. + if (!path || path.length===0) throw new Error('Invalid Path'); + + return [ target, path ]; +}; + +var getPath = Ember._getPath = function(root, path) { + var hasThis, parts, tuple, idx, len; + + // If there is no root and path is a key name, return that + // property from the global object. + // E.g. get('Ember') -> Ember + if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); } + + // detect complicated paths and normalize them + hasThis = HAS_THIS.test(path); + + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; + } + + parts = path.split("."); + len = parts.length; + for (idx = 0; root != null && idx < len; idx++) { + root = get(root, parts[idx], true); + if (root && root.isDestroyed) { return undefined; } + } + return root; +}; + +Ember.getWithDefault = function(root, key, defaultValue) { + var value = get(root, key); + + if (value === undefined) { return defaultValue; } + return value; +}; + + +Ember.get = get; +Ember.getPath = Ember.deprecateFunc('getPath is deprecated since get now supports paths', Ember.get); + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +var o_create = Ember.create, + metaFor = Ember.meta, + META_KEY = Ember.META_KEY, + /* listener flags */ + ONCE = 1, SUSPENDED = 2; + +/* + The event system uses a series of nested hashes to store listeners on an + object. When a listener is registered, or when an event arrives, these + hashes are consulted to determine which target and action pair to invoke. + + The hashes are stored in the object's meta hash, and look like this: + + // Object's meta hash + { + listeners: { // variable name: `listenerSet` + "foo:changed": [ // variable name: `actions` + [target, method, flags] + ] + } + } + +*/ + +function indexOf(array, target, method) { + var index = -1; + for (var i = 0, l = array.length; i < l; i++) { + if (target === array[i][0] && method === array[i][1]) { index = i; break; } + } + return index; +} + +function actionsFor(obj, eventName) { + var meta = metaFor(obj, true), + actions; + + if (!meta.listeners) { meta.listeners = {}; } + + if (!meta.hasOwnProperty('listeners')) { + // setup inherited copy of the listeners object + meta.listeners = o_create(meta.listeners); + } + + actions = meta.listeners[eventName]; + + // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype + if (actions && !meta.listeners.hasOwnProperty(eventName)) { + actions = meta.listeners[eventName] = meta.listeners[eventName].slice(); + } else if (!actions) { + actions = meta.listeners[eventName] = []; + } + + return actions; +} + +function actionsUnion(obj, eventName, otherActions) { + var meta = obj[META_KEY], + actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { return; } + for (var i = actions.length - 1; i >= 0; i--) { + var target = actions[i][0], + method = actions[i][1], + flags = actions[i][2], + actionIndex = indexOf(otherActions, target, method); + + if (actionIndex === -1) { + otherActions.push([target, method, flags]); + } + } +} + +function actionsDiff(obj, eventName, otherActions) { + var meta = obj[META_KEY], + actions = meta && meta.listeners && meta.listeners[eventName], + diffActions = []; + + if (!actions) { return; } + for (var i = actions.length - 1; i >= 0; i--) { + var target = actions[i][0], + method = actions[i][1], + flags = actions[i][2], + actionIndex = indexOf(otherActions, target, method); + + if (actionIndex !== -1) { continue; } + + otherActions.push([target, method, flags]); + diffActions.push([target, method, flags]); + } + + return diffActions; +} + +/** + Add an event listener + + @method addListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} targetOrMethod A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Boolean} once A flag whether a function should only be called once +*/ +function addListener(obj, eventName, target, method, once) { + Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); + + if (!method && 'function' === typeof target) { + method = target; + target = null; + } + + var actions = actionsFor(obj, eventName), + actionIndex = indexOf(actions, target, method), + flags = 0; + + if (once) flags |= ONCE; + + if (actionIndex !== -1) { return; } + + actions.push([target, method, flags]); + + if ('function' === typeof obj.didAddListener) { + obj.didAddListener(eventName, target, method); + } +} + +/** + Remove an event listener + + Arguments should match those passed to `Ember.addListener`. + + @method removeListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} targetOrMethod A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` +*/ +function removeListener(obj, eventName, target, method) { + Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); + + if (!method && 'function' === typeof target) { + method = target; + target = null; + } + + function _removeListener(target, method) { + var actions = actionsFor(obj, eventName), + actionIndex = indexOf(actions, target, method); + + // action doesn't exist, give up silently + if (actionIndex === -1) { return; } + + actions.splice(actionIndex, 1); + + if ('function' === typeof obj.didRemoveListener) { + obj.didRemoveListener(eventName, target, method); + } + } + + if (method) { + _removeListener(target, method); + } else { + var meta = obj[META_KEY], + actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { return; } + for (var i = actions.length - 1; i >= 0; i--) { + _removeListener(actions[i][0], actions[i][1]); + } + } +} + +/** + @private + + Suspend listener during callback. + + This should only be used by the target of the event listener + when it is taking an action that would cause the event, e.g. + an object might suspend its property change listener while it is + setting that property. + + @method suspendListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} targetOrMethod A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback +*/ +function suspendListener(obj, eventName, target, method, callback) { + if (!method && 'function' === typeof target) { + method = target; + target = null; + } + + var actions = actionsFor(obj, eventName), + actionIndex = indexOf(actions, target, method), + action; + + if (actionIndex !== -1) { + action = actions[actionIndex].slice(); // copy it, otherwise we're modifying a shared object + action[2] |= SUSPENDED; // mark the action as suspended + actions[actionIndex] = action; // replace the shared object with our copy + } + + function tryable() { return callback.call(target); } + function finalizer() { if (action) { action[2] &= ~SUSPENDED; } } + + return Ember.tryFinally(tryable, finalizer); +} + +/** + @private + + Suspend listener during callback. + + This should only be used by the target of the event listener + when it is taking an action that would cause the event, e.g. + an object might suspend its property change listener while it is + setting that property. + + @method suspendListener + @for Ember + @param obj + @param {Array} eventName Array of event names + @param {Object|Function} targetOrMethod A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback +*/ +function suspendListeners(obj, eventNames, target, method, callback) { + if (!method && 'function' === typeof target) { + method = target; + target = null; + } + + var suspendedActions = [], + eventName, actions, action, i, l; + + for (i=0, l=eventNames.length; i<l; i++) { + eventName = eventNames[i]; + actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + if (actionIndex !== -1) { + action = actions[actionIndex].slice(); + action[2] |= SUSPENDED; + actions[actionIndex] = action; + suspendedActions.push(action); + } + } + + function tryable() { return callback.call(target); } + + function finalizer() { + for (i = 0, l = suspendedActions.length; i < l; i++) { + suspendedActions[i][2] &= ~SUSPENDED; + } + } + + return Ember.tryFinally(tryable, finalizer); +} + +/** + @private + + Return a list of currently watched events + + @method watchedEvents + @for Ember + @param obj +*/ +function watchedEvents(obj) { + var listeners = obj[META_KEY].listeners, ret = []; + + if (listeners) { + for(var eventName in listeners) { + if (listeners[eventName]) { ret.push(eventName); } + } + } + return ret; +} + +/** + @method sendEvent + @for Ember + @param obj + @param {String} eventName + @param {Array} params + @param {Array} actions + @return true +*/ +function sendEvent(obj, eventName, params, actions) { + // first give object a chance to handle it + if (obj !== Ember && 'function' === typeof obj.sendEvent) { + obj.sendEvent(eventName, params); + } + + if (!actions) { + var meta = obj[META_KEY]; + actions = meta && meta.listeners && meta.listeners[eventName]; + } + + if (!actions) { return; } + + for (var i = actions.length - 1; i >= 0; i--) { // looping in reverse for once listeners + var action = actions[i]; + if (!action) { continue; } + var target = action[0], method = action[1], flags = action[2]; + if (flags & SUSPENDED) { continue; } + if (flags & ONCE) { removeListener(obj, eventName, target, method); } + if (!target) { target = obj; } + if ('string' === typeof method) { method = target[method]; } + if (params) { + method.apply(target, params); + } else { + method.call(target); + } + } + return true; +} + +/** + @private + @method hasListeners + @for Ember + @param obj + @param {String} eventName +*/ +function hasListeners(obj, eventName) { + var meta = obj[META_KEY], + actions = meta && meta.listeners && meta.listeners[eventName]; + + return !!(actions && actions.length); +} + +/** + @private + @method listenersFor + @for Ember + @param obj + @param {String} eventName +*/ +function listenersFor(obj, eventName) { + var ret = []; + var meta = obj[META_KEY], + actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { return ret; } + + for (var i = 0, l = actions.length; i < l; i++) { + var target = actions[i][0], + method = actions[i][1]; + ret.push([target, method]); + } + + return ret; +} + +Ember.addListener = addListener; +Ember.removeListener = removeListener; +Ember._suspendListener = suspendListener; +Ember._suspendListeners = suspendListeners; +Ember.sendEvent = sendEvent; +Ember.hasListeners = hasListeners; +Ember.watchedEvents = watchedEvents; +Ember.listenersFor = listenersFor; +Ember.listenersDiff = actionsDiff; +Ember.listenersUnion = actionsUnion; + +})(); + + + +(function() { +var guidFor = Ember.guidFor, + sendEvent = Ember.sendEvent; + +/* + this.observerSet = { + [senderGuid]: { // variable name: `keySet` + [keyName]: listIndex + } + }, + this.observers = [ + { + sender: obj, + keyName: keyName, + eventName: eventName, + listeners: [ + [target, method, flags] + ] + }, + ... + ] +*/ +var ObserverSet = Ember._ObserverSet = function() { + this.clear(); +}; + +ObserverSet.prototype.add = function(sender, keyName, eventName) { + var observerSet = this.observerSet, + observers = this.observers, + senderGuid = guidFor(sender), + keySet = observerSet[senderGuid], + index; + + if (!keySet) { + observerSet[senderGuid] = keySet = {}; + } + index = keySet[keyName]; + if (index === undefined) { + index = observers.push({ + sender: sender, + keyName: keyName, + eventName: eventName, + listeners: [] + }) - 1; + keySet[keyName] = index; + } + return observers[index].listeners; +}; + +ObserverSet.prototype.flush = function() { + var observers = this.observers, i, len, observer, sender; + this.clear(); + for (i=0, len=observers.length; i < len; ++i) { + observer = observers[i]; + sender = observer.sender; + if (sender.isDestroying || sender.isDestroyed) { continue; } + sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); + } +}; + +ObserverSet.prototype.clear = function() { + this.observerSet = {}; + this.observers = []; +}; +})(); + + + +(function() { +var metaFor = Ember.meta, + guidFor = Ember.guidFor, + tryFinally = Ember.tryFinally, + sendEvent = Ember.sendEvent, + listenersUnion = Ember.listenersUnion, + listenersDiff = Ember.listenersDiff, + ObserverSet = Ember._ObserverSet, + beforeObserverSet = new ObserverSet(), + observerSet = new ObserverSet(), + deferred = 0; + +// .......................................................... +// PROPERTY CHANGES +// + +/** + This function is called just before an object property is about to change. + It will notify any before observers and prepare caches among other things. + + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyDidChange()` which you should call just + after the property value changes. + + @method propertyWillChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} +*/ +var propertyWillChange = Ember.propertyWillChange = function(obj, keyName) { + var m = metaFor(obj, false), + watching = m.watching[keyName] > 0 || keyName === 'length', + proto = m.proto, + desc = m.descs[keyName]; + + if (!watching) { return; } + if (proto === obj) { return; } + if (desc && desc.willChange) { desc.willChange(obj, keyName); } + dependentKeysWillChange(obj, keyName, m); + chainsWillChange(obj, keyName, m); + notifyBeforeObservers(obj, keyName); +}; + +/** + This function is called just after an object property has changed. + It will notify any observers and clear caches among other things. + + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyWilLChange()` which you should call just + before the property value changes. + + @method propertyDidChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} +*/ +var propertyDidChange = Ember.propertyDidChange = function(obj, keyName) { + var m = metaFor(obj, false), + watching = m.watching[keyName] > 0 || keyName === 'length', + proto = m.proto, + desc = m.descs[keyName]; + + if (proto === obj) { return; } + + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { desc.didChange(obj, keyName); } + if (!watching && keyName !== 'length') { return; } + + dependentKeysDidChange(obj, keyName, m); + chainsDidChange(obj, keyName, m); + notifyObservers(obj, keyName); +}; + +var WILL_SEEN, DID_SEEN; + +// called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) +function dependentKeysWillChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } + + var seen = WILL_SEEN, top = !seen; + if (top) { seen = WILL_SEEN = {}; } + iterDeps(propertyWillChange, obj, depKey, seen, meta); + if (top) { WILL_SEEN = null; } +} + +// called whenever a property has just changed to update dependent keys +function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } + + var seen = DID_SEEN, top = !seen; + if (top) { seen = DID_SEEN = {}; } + iterDeps(propertyDidChange, obj, depKey, seen, meta); + if (top) { DID_SEEN = null; } +} + +function iterDeps(method, obj, depKey, seen, meta) { + var guid = guidFor(obj); + if (!seen[guid]) seen[guid] = {}; + if (seen[guid][depKey]) return; + seen[guid][depKey] = true; + + var deps = meta.deps; + deps = deps && deps[depKey]; + if (deps) { + for(var key in deps) { + var desc = meta.descs[key]; + if (desc && desc._suspended === obj) continue; + method(obj, key); + } + } +} + +var chainsWillChange = function(obj, keyName, m, arg) { + if (!m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + + var nodes = m.chainWatchers; + + nodes = nodes[keyName]; + if (!nodes) { return; } + + for(var i = 0, l = nodes.length; i < l; i++) { + nodes[i].willChange(arg); + } +}; + +var chainsDidChange = function(obj, keyName, m, arg) { + if (!m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + + var nodes = m.chainWatchers; + + nodes = nodes[keyName]; + if (!nodes) { return; } + + // looping in reverse because the chainWatchers array can be modified inside didChange + for (var i = nodes.length - 1; i >= 0; i--) { + nodes[i].didChange(arg); + } +}; + +Ember.overrideChains = function(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); +}; + +/** + @method beginPropertyChanges + @chainable +*/ +var beginPropertyChanges = Ember.beginPropertyChanges = function() { + deferred++; +}; + +/** + @method endPropertyChanges +*/ +var endPropertyChanges = Ember.endPropertyChanges = function() { + deferred--; + if (deferred<=0) { + beforeObserverSet.clear(); + observerSet.flush(); + } +}; + +/** + Make a series of property changes together in an + exception-safe way. + + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` + + @method changeProperties + @param {Function} callback + @param [binding] +*/ +Ember.changeProperties = function(cb, binding){ + beginPropertyChanges(); + tryFinally(cb, endPropertyChanges, binding); +}; + +var notifyBeforeObservers = function(obj, keyName) { + if (obj.isDestroying) { return; } + + var eventName = keyName + ':before', listeners, diff; + if (deferred) { + listeners = beforeObserverSet.add(obj, keyName, eventName); + diff = listenersDiff(obj, eventName, listeners); + sendEvent(obj, eventName, [obj, keyName], diff); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } +}; + +var notifyObservers = function(obj, keyName) { + if (obj.isDestroying) { return; } + + var eventName = keyName + ':change', listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + listenersUnion(obj, eventName, listeners); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } +}; +})(); + + + +(function() { +// META_KEY +// _getPath +// propertyWillChange, propertyDidChange + +var META_KEY = Ember.META_KEY, + MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, + IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/, + getPath = Ember._getPath; + +/** + Sets the value of a property on an object, respecting computed properties + and notifying observers and other listeners of the change. If the + property is not defined but the object implements the `unknownProperty` + method then that will be invoked as well. + + If you plan to run on IE8 and older browsers then you should use this + method anytime you want to set a property on an object that you don't + know for sure is private. (Properties beginning with an underscore '_' + are considered private.) + + On all newer browsers, you only need to use this method to set + properties if the property might not be defined on the object and you want + to respect the `unknownProperty` handler. Otherwise you can ignore this + method. + + @method set + @for Ember + @param {Object} obj The object to modify. + @param {String} keyName The property key to set + @param {Object} value The value to set + @return {Object} the passed value. +*/ +var set = function set(obj, keyName, value, tolerant) { + if (typeof obj === 'string') { + Ember.assert("Path '" + obj + "' must be global if no obj is given.", IS_GLOBAL.test(obj)); + value = keyName; + keyName = obj; + obj = null; + } + + Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); + + if (!obj || keyName.indexOf('.') !== -1) { + return setPath(obj, keyName, value, tolerant); + } + + Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); + Ember.assert('calling set on destroyed object', !obj.isDestroyed); + + var meta = obj[META_KEY], desc = meta && meta.descs[keyName], + isUnknown, currentValue; + if (desc) { + desc.set(obj, keyName, value); + } else { + isUnknown = 'object' === typeof obj && !(keyName in obj); + + // setUnknownProperty is called if `obj` is an object, + // the property does not already exist, and the + // `setUnknownProperty` method exists on the object + if (isUnknown && 'function' === typeof obj.setUnknownProperty) { + obj.setUnknownProperty(keyName, value); + } else if (meta && meta.watching[keyName] > 0) { + if (MANDATORY_SETTER) { + currentValue = meta.values[keyName]; + } else { + currentValue = obj[keyName]; + } + // only trigger a change if the value has changed + if (value !== currentValue) { + Ember.propertyWillChange(obj, keyName); + if (MANDATORY_SETTER) { + if (currentValue === undefined && !(keyName in obj)) { + Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter + } else { + meta.values[keyName] = value; + } + } else { + obj[keyName] = value; + } + Ember.propertyDidChange(obj, keyName); + } + } else { + obj[keyName] = value; + } + } + return value; +}; + +// Currently used only by Ember Data tests +if (Ember.config.overrideAccessors) { + Ember.set = set; + Ember.config.overrideAccessors(); + set = Ember.set; +} + +function setPath(root, path, value, tolerant) { + var keyName; + + // get the last part of the path + keyName = path.slice(path.lastIndexOf('.') + 1); + + // get the first part of the part + path = path.slice(0, path.length-(keyName.length+1)); + + // unless the path is this, look up the first part to + // get the root + if (path !== 'this') { + root = getPath(root, path); + } + + if (!keyName || keyName.length === 0) { + throw new Error('You passed an empty path'); + } + + if (!root) { + if (tolerant) { return; } + else { throw new Error('Object in path '+path+' could not be found or was destroyed.'); } + } + + return set(root, keyName, value); +} + +Ember.set = set; +Ember.setPath = Ember.deprecateFunc('setPath is deprecated since set now supports paths', Ember.set); + +/** + Error-tolerant form of `Ember.set`. Will not blow up if any part of the + chain is `undefined`, `null`, or destroyed. + + This is primarily used when syncing bindings, which may try to update after + an object has been destroyed. + + @method trySet + @for Ember + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set +*/ +Ember.trySet = function(root, path, value) { + return set(root, path, value, true); +}; +Ember.trySetPath = Ember.deprecateFunc('trySetPath has been renamed to trySet', Ember.trySet); + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +/* + JavaScript (before ES6) does not have a Map implementation. Objects, + which are often used as dictionaries, may only have Strings as keys. + + Because Ember has a way to get a unique identifier for every object + via `Ember.guidFor`, we can implement a performant Map with arbitrary + keys. Because it is commonly used in low-level bookkeeping, Map is + implemented as a pure JavaScript object for performance. + + This implementation follows the current iteration of the ES6 proposal for + maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), + with two exceptions. First, because we need our implementation to be pleasant + on older browsers, we do not use the `delete` name (using `remove` instead). + Second, as we do not have the luxury of in-VM iteration, we implement a + forEach method for iteration. + + Map is mocked out to look like an Ember object, so you can do + `Ember.Map.create()` for symmetry with other Ember classes. +*/ +var get = Ember.get, + set = Ember.set, + guidFor = Ember.guidFor, + indexOf = Ember.ArrayPolyfills.indexOf; + +var copy = function(obj) { + var output = {}; + + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; } + } + + return output; +}; + +var copyMap = function(original, newObject) { + var keys = original.keys.copy(), + values = copy(original.values); + + newObject.keys = keys; + newObject.values = values; + newObject.length = original.length; + + return newObject; +}; + +/** + This class is used internally by Ember and Ember Data. + Please do not use it at this time. We plan to clean it up + and add many tests soon. + + @class OrderedSet + @namespace Ember + @constructor + @private +*/ +var OrderedSet = Ember.OrderedSet = function() { + this.clear(); +}; + +/** + @method create + @static + @return {Ember.OrderedSet} +*/ +OrderedSet.create = function() { + return new OrderedSet(); +}; + + +OrderedSet.prototype = { + /** + @method clear + */ + clear: function() { + this.presenceSet = {}; + this.list = []; + }, + + /** + @method add + @param obj + */ + add: function(obj) { + var guid = guidFor(obj), + presenceSet = this.presenceSet, + list = this.list; + + if (guid in presenceSet) { return; } + + presenceSet[guid] = true; + list.push(obj); + }, + + /** + @method remove + @param obj + */ + remove: function(obj) { + var guid = guidFor(obj), + presenceSet = this.presenceSet, + list = this.list; + + delete presenceSet[guid]; + + var index = indexOf.call(list, obj); + if (index > -1) { + list.splice(index, 1); + } + }, + + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function() { + return this.list.length === 0; + }, + + /** + @method has + @param obj + @return {Boolean} + */ + has: function(obj) { + var guid = guidFor(obj), + presenceSet = this.presenceSet; + + return guid in presenceSet; + }, + + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function(fn, self) { + // allow mutation during iteration + var list = this.toArray(); + + for (var i = 0, j = list.length; i < j; i++) { + fn.call(self, list[i]); + } + }, + + /** + @method toArray + @return {Array} + */ + toArray: function() { + return this.list.slice(); + }, + + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function() { + var set = new OrderedSet(); + + set.presenceSet = copy(this.presenceSet); + set.list = this.toArray(); + + return set; + } +}; + +/** + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. + + Internally, a Map has two data structures: + + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + + When a key/value pair is added for the first time, we + add the key to the `keys` OrderedSet, and create or + replace an entry in `values`. When an entry is deleted, + we delete its entry in `keys` and `values`. + + @class Map + @namespace Ember + @private + @constructor +*/ +var Map = Ember.Map = function() { + this.keys = Ember.OrderedSet.create(); + this.values = {}; +}; + +/** + @method create + @static +*/ +Map.create = function() { + return new Map(); +}; + +Map.prototype = { + /** + This property will change as the number of objects in the map changes. + + @property length + @type number + @default 0 + */ + length: 0, + + + /** + Retrieve the value associated with a given key. + + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function(key) { + var values = this.values, + guid = guidFor(key); + + return values[guid]; + }, + + /** + Adds a value to the map. If a value for the given key has already been + provided, the new value will replace the old value. + + @method set + @param {*} key + @param {*} value + */ + set: function(key, value) { + var keys = this.keys, + values = this.values, + guid = guidFor(key); + + keys.add(key); + values[guid] = value; + set(this, 'length', keys.list.length); + }, + + /** + Removes a value from the map for an associated key. + + @method remove + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + remove: function(key) { + // don't use ES6 "delete" because it will be annoying + // to use in browsers that are not ES6 friendly; + var keys = this.keys, + values = this.values, + guid = guidFor(key); + + if (values.hasOwnProperty(guid)) { + keys.remove(key); + delete values[guid]; + set(this, 'length', keys.list.length); + return true; + } else { + return false; + } + }, + + /** + Check whether a key is present. + + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function(key) { + var values = this.values, + guid = guidFor(key); + + return values.hasOwnProperty(guid); + }, + + /** + Iterate over all the keys and values. Calls the function once + for each key, passing in the key and value, in that order. + + The keys are guaranteed to be iterated over in insertion order. + + @method forEach + @param {Function} callback + @param {*} self if passed, the `this` value inside the + callback. By default, `this` is the map. + */ + forEach: function(callback, self) { + var keys = this.keys, + values = this.values; + + keys.forEach(function(key) { + var guid = guidFor(key); + callback.call(self, key, values[guid]); + }); + }, + + /** + @method copy + @return {Ember.Map} + */ + copy: function() { + return copyMap(this, new Map()); + } +}; + +/** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] +*/ +var MapWithDefault = Ember.MapWithDefault = function(options) { + Map.call(this); + this.defaultValue = options.defaultValue; +}; + +/** + @method create + @static + @param [options] + @param {*} [options.defaultValue] + @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns + `Ember.MapWithDefault` otherwise returns `Ember.Map` +*/ +MapWithDefault.create = function(options) { + if (options) { + return new MapWithDefault(options); + } else { + return new Map(); + } +}; + +MapWithDefault.prototype = Ember.create(Map.prototype); + +/** + Retrieve the value associated with a given key. + + @method get + @param {*} key + @return {*} the value associated with the key, or the default value +*/ +MapWithDefault.prototype.get = function(key) { + var hasValue = this.has(key); + + if (hasValue) { + return Map.prototype.get.call(this, key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; + } +}; + +/** + @method copy + @return {Ember.MapWithDefault} +*/ +MapWithDefault.prototype.copy = function() { + return copyMap(this, new MapWithDefault({ + defaultValue: this.defaultValue + })); +}; + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +var META_KEY = Ember.META_KEY, + metaFor = Ember.meta, + objectDefineProperty = Ember.platform.defineProperty; + +var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + +// .......................................................... +// DESCRIPTOR +// + +/** + Objects of this type can implement an interface to respond to requests to + get and set. The default implementation handles simple properties. + + You generally won't need to create or subclass this directly. + + @class Descriptor + @namespace Ember + @private + @constructor +*/ +Ember.Descriptor = function() {}; + +// .......................................................... +// DEFINING PROPERTIES API +// + +var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) { + Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false); +}; + +var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) { + return function() { + var meta = this[META_KEY]; + return meta && meta.values[name]; + }; +}; + +/** + @private + + NOTE: This is a low-level method used by other parts of the API. You almost + never want to call this method directly. Instead you should use + `Ember.mixin()` to define new properties. + + Defines a property on an object. This method works much like the ES5 + `Object.defineProperty()` method except that it can also accept computed + properties and other special descriptors. + + Normally this method takes only three parameters. However if you pass an + instance of `Ember.Descriptor` as the third param then you can pass an + optional value as the fourth parameter. This is often more efficient than + creating new descriptor hashes for each property. + + ## Examples + + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); + + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` + + @method defineProperty + @for Ember + @param {Object} obj the object to define this property on. This may be a prototype. + @param {String} keyName the name of the property + @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a + computed property) or an ES5 descriptor. + You must provide this or `data` but not both. + @param {*} [data] something other than a descriptor, that will + become the explicit value of this property. +*/ +Ember.defineProperty = function(obj, keyName, desc, data, meta) { + var descs, existingDesc, watching, value; + + if (!meta) meta = metaFor(obj); + descs = meta.descs; + existingDesc = meta.descs[keyName]; + watching = meta.watching[keyName] > 0; + + if (existingDesc instanceof Ember.Descriptor) { + existingDesc.teardown(obj, keyName); + } + + if (desc instanceof Ember.Descriptor) { + value = desc; + + descs[keyName] = desc; + if (MANDATORY_SETTER && watching) { + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: undefined // make enumerable + }); + } else { + obj[keyName] = undefined; // make enumerable + } + desc.setup(obj, keyName); + } else { + descs[keyName] = undefined; // shadow descriptor in proto + if (desc == null) { + value = data; + + if (MANDATORY_SETTER && watching) { + meta.values[keyName] = data; + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: MANDATORY_SETTER_FUNCTION, + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } else { + obj[keyName] = data; + } + } else { + value = desc; + + // compatibility with ES5 + objectDefineProperty(obj, keyName, desc); + } + } + + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { Ember.overrideChains(obj, keyName, meta); } + + // The `value` passed to the `didDefineProperty` hook is + // either the descriptor or data, whichever was passed. + if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } + + return this; +}; + + +})(); + + + +(function() { +var changeProperties = Ember.changeProperties, + set = Ember.set; + +/** + Set a list of properties on an object. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. + + @method setProperties + @param self + @param {Object} hash + @return self +*/ +Ember.setProperties = function(self, hash) { + changeProperties(function(){ + for(var prop in hash) { + if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); } + } + }); + return self; +}; +})(); + + + +(function() { +var metaFor = Ember.meta, // utils.js + typeOf = Ember.typeOf, // utils.js + MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, + o_defineProperty = Ember.platform.defineProperty; + +Ember.watchKey = function(obj, keyName) { + // can't watch length on Array - it is special... + if (keyName === 'length' && typeOf(obj) === 'array') { return; } + + var m = metaFor(obj), watching = m.watching, desc; + + // activate watching first time + if (!watching[keyName]) { + watching[keyName] = 1; + desc = m.descs[keyName]; + if (desc && desc.willWatch) { desc.willWatch(obj, keyName); } + + if ('function' === typeof obj.willWatchProperty) { + obj.willWatchProperty(keyName); + } + + if (MANDATORY_SETTER && keyName in obj) { + m.values[keyName] = obj[keyName]; + o_defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: Ember.MANDATORY_SETTER_FUNCTION, + get: Ember.DEFAULT_GETTER_FUNCTION(keyName) + }); + } + } else { + watching[keyName] = (watching[keyName] || 0) + 1; + } +}; + + +Ember.unwatchKey = function(obj, keyName) { + var m = metaFor(obj), watching = m.watching, desc; + + if (watching[keyName] === 1) { + watching[keyName] = 0; + desc = m.descs[keyName]; + + if (desc && desc.didUnwatch) { desc.didUnwatch(obj, keyName); } + + if ('function' === typeof obj.didUnwatchProperty) { + obj.didUnwatchProperty(keyName); + } + + if (MANDATORY_SETTER && keyName in obj) { + o_defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: m.values[keyName] + }); + delete m.values[keyName]; + } + } else if (watching[keyName] > 1) { + watching[keyName]--; + } +}; +})(); + + + +(function() { +var metaFor = Ember.meta, // utils.js + get = Ember.get, // property_get.js + normalizeTuple = Ember.normalizeTuple, // property_get.js + forEach = Ember.ArrayPolyfills.forEach, // array.js + warn = Ember.warn, + watchKey = Ember.watchKey, + unwatchKey = Ember.unwatchKey, + propertyWillChange = Ember.propertyWillChange, + propertyDidChange = Ember.propertyDidChange, + FIRST_KEY = /^([^\.\*]+)/; + +function firstKey(path) { + return path.match(FIRST_KEY)[0]; +} + +var pendingQueue = []; + +// attempts to add the pendingQueue chains again. If some of them end up +// back in the queue and reschedule is true, schedules a timeout to try +// again. +Ember.flushPendingChains = function() { + if (pendingQueue.length === 0) { return; } // nothing to do + + var queue = pendingQueue; + pendingQueue = []; + + forEach.call(queue, function(q) { q[0].add(q[1]); }); + + warn('Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos', pendingQueue.length === 0); +}; + + +function addChainWatcher(obj, keyName, node) { + if (!obj || ('object' !== typeof obj)) { return; } // nothing to do + + var m = metaFor(obj), nodes = m.chainWatchers; + + if (!m.hasOwnProperty('chainWatchers')) { + nodes = m.chainWatchers = {}; + } + + if (!nodes[keyName]) { nodes[keyName] = []; } + nodes[keyName].push(node); + watchKey(obj, keyName); +} + +var removeChainWatcher = Ember.removeChainWatcher = function(obj, keyName, node) { + if (!obj || 'object' !== typeof obj) { return; } // nothing to do + + var m = metaFor(obj, false); + if (!m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + + var nodes = m.chainWatchers; + + if (nodes[keyName]) { + nodes = nodes[keyName]; + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i] === node) { nodes.splice(i, 1); } + } + } + unwatchKey(obj, keyName); +}; + +function isProto(pvalue) { + return metaFor(pvalue, false).proto === pvalue; +} + +// A ChainNode watches a single key on an object. If you provide a starting +// value for the key then the node won't actually watch it. For a root node +// pass null for parent and key and object for value. +var ChainNode = Ember._ChainNode = function(parent, key, value) { + this._parent = parent; + this._key = key; + + // _watching is true when calling get(this._parent, this._key) will + // return the value of this node. + // + // It is false for the root of a chain (because we have no parent) + // and for global paths (because the parent node is the object with + // the observer on it) + this._watching = value===undefined; + + this._value = value; + this._paths = {}; + if (this._watching) { + this._object = parent.value(); + if (this._object) { addChainWatcher(this._object, this._key, this); } + } + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + // + // TODO: Replace this with an efficient callback that the EachProxy + // can implement. + if (this._parent && this._parent._key === '@each') { + this.value(); + } +}; + +var ChainNodePrototype = ChainNode.prototype; + +ChainNodePrototype.value = function() { + if (this._value === undefined && this._watching) { + var obj = this._parent.value(); + this._value = (obj && !isProto(obj)) ? get(obj, this._key) : undefined; + } + return this._value; +}; + +ChainNodePrototype.destroy = function() { + if (this._watching) { + var obj = this._object; + if (obj) { removeChainWatcher(obj, this._key, this); } + this._watching = false; // so future calls do nothing + } +}; + +// copies a top level object only +ChainNodePrototype.copy = function(obj) { + var ret = new ChainNode(null, null, obj), + paths = this._paths, path; + for (path in paths) { + if (paths[path] <= 0) { continue; } // this check will also catch non-number vals. + ret.add(path); + } + return ret; +}; + +// called on the root node of a chain to setup watchers on the specified +// path. +ChainNodePrototype.add = function(path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + paths[path] = (paths[path] || 0) + 1; + + obj = this.value(); + tuple = normalizeTuple(obj, path); + + // the path was a local path + if (tuple[0] && tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length+1); + + // global path, but object does not exist yet. + // put into a queue and try to connect later. + } else if (!tuple[0]) { + pendingQueue.push([this, path]); + tuple.length = 0; + return; + + // global path, and object already exists + } else { + src = tuple[0]; + key = path.slice(0, 0-(tuple[1].length+1)); + path = tuple[1]; + } + + tuple.length = 0; + this.chain(key, path, src); +}; + +// called on the root node of a chain to teardown watcher on the specified +// path +ChainNodePrototype.remove = function(path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + if (paths[path] > 0) { paths[path]--; } + + obj = this.value(); + tuple = normalizeTuple(obj, path); + if (tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length+1); + } else { + src = tuple[0]; + key = path.slice(0, 0-(tuple[1].length+1)); + path = tuple[1]; + } + + tuple.length = 0; + this.unchain(key, path); +}; + +ChainNodePrototype.count = 0; + +ChainNodePrototype.chain = function(key, path, src) { + var chains = this._chains, node; + if (!chains) { chains = this._chains = {}; } + + node = chains[key]; + if (!node) { node = chains[key] = new ChainNode(this, key, src); } + node.count++; // count chains... + + // chain rest of path if there is one + if (path && path.length>0) { + key = firstKey(path); + path = path.slice(key.length+1); + node.chain(key, path); // NOTE: no src means it will observe changes... + } +}; + +ChainNodePrototype.unchain = function(key, path) { + var chains = this._chains, node = chains[key]; + + // unchain rest of path first... + if (path && path.length>1) { + key = firstKey(path); + path = path.slice(key.length+1); + node.unchain(key, path); + } + + // delete node if needed. + node.count--; + if (node.count<=0) { + delete chains[node._key]; + node.destroy(); + } + +}; + +ChainNodePrototype.willChange = function() { + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { continue; } + chains[key].willChange(); + } + } + + if (this._parent) { this._parent.chainWillChange(this, this._key, 1); } +}; + +ChainNodePrototype.chainWillChange = function(chain, path, depth) { + if (this._key) { path = this._key + '.' + path; } + + if (this._parent) { + this._parent.chainWillChange(this, path, depth+1); + } else { + if (depth > 1) { propertyWillChange(this.value(), path); } + path = 'this.' + path; + if (this._paths[path] > 0) { propertyWillChange(this.value(), path); } + } +}; + +ChainNodePrototype.chainDidChange = function(chain, path, depth) { + if (this._key) { path = this._key + '.' + path; } + if (this._parent) { + this._parent.chainDidChange(this, path, depth+1); + } else { + if (depth > 1) { propertyDidChange(this.value(), path); } + path = 'this.' + path; + if (this._paths[path] > 0) { propertyDidChange(this.value(), path); } + } +}; + +ChainNodePrototype.didChange = function(suppressEvent) { + // invalidate my own value first. + if (this._watching) { + var obj = this._parent.value(); + if (obj !== this._object) { + removeChainWatcher(this._object, this._key, this); + this._object = obj; + addChainWatcher(obj, this._key, this); + } + this._value = undefined; + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + if (this._parent && this._parent._key === '@each') + this.value(); + } + + // then notify chains... + var chains = this._chains; + if (chains) { + for(var key in chains) { + if (!chains.hasOwnProperty(key)) { continue; } + chains[key].didChange(suppressEvent); + } + } + + if (suppressEvent) { return; } + + // and finally tell parent about my path changing... + if (this._parent) { this._parent.chainDidChange(this, this._key, 1); } +}; + +Ember.finishChains = function(obj) { + var m = metaFor(obj, false), chains = m.chains; + if (chains) { + if (chains.value() !== obj) { + m.chains = chains = chains.copy(obj); + } + chains.didChange(true); + } +}; +})(); + + + +(function() { +var metaFor = Ember.meta, // utils.js + typeOf = Ember.typeOf, // utils.js + ChainNode = Ember._ChainNode; // chains.js + +// get the chains for the current object. If the current object has +// chains inherited from the proto they will be cloned and reconfigured for +// the current object. +function chainsFor(obj) { + var m = metaFor(obj), ret = m.chains; + if (!ret) { + ret = m.chains = new ChainNode(null, null, obj); + } else if (ret.value() !== obj) { + ret = m.chains = ret.copy(obj); + } + return ret; +} + +Ember.watchPath = function(obj, keyPath) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + + var m = metaFor(obj), watching = m.watching; + + if (!watching[keyPath]) { // activate watching first time + watching[keyPath] = 1; + chainsFor(obj).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } +}; + +Ember.unwatchPath = function(obj, keyPath) { + var m = metaFor(obj), watching = m.watching; + + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } +}; +})(); + + + +(function() { +/** +@module ember-metal +*/ + +var metaFor = Ember.meta, // utils.js + GUID_KEY = Ember.GUID_KEY, // utils.js + META_KEY = Ember.META_KEY, // utils.js + removeChainWatcher = Ember.removeChainWatcher, + watchKey = Ember.watchKey, // watch_key.js + unwatchKey = Ember.unwatchKey, + watchPath = Ember.watchPath, // watch_path.js + unwatchPath = Ember.unwatchPath, + typeOf = Ember.typeOf, // utils.js + generateGuid = Ember.generateGuid, + IS_PATH = /[\.\*]/; + +// returns true if the passed path is just a keyName +function isKeyName(path) { + return path==='*' || !IS_PATH.test(path); +} + +/** + @private + + Starts watching a property on an object. Whenever the property changes, + invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the + primitive used by observers and dependent keys; usually you will never call + this method directly but instead use higher level methods like + `Ember.addObserver()` + + @method watch + @for Ember + @param obj + @param {String} keyName +*/ +Ember.watch = function(obj, keyPath) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + + if (isKeyName(keyPath)) { + watchKey(obj, keyPath); + } else { + watchPath(obj, keyPath); + } +}; + +Ember.isWatching = function isWatching(obj, key) { + var meta = obj[META_KEY]; + return (meta && meta.watching[key]) > 0; +}; + +Ember.watch.flushPending = Ember.flushPendingChains; + +Ember.unwatch = function(obj, keyPath) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + + if (isKeyName(keyPath)) { + unwatchKey(obj, keyPath); + } else { + unwatchPath(obj, keyPath); + } +}; + +/** + @private + + Call on an object when you first beget it from another object. This will + setup any chained watchers on the object instance as needed. This method is + safe to call multiple times. + + @method rewatch + @for Ember + @param obj +*/ +Ember.rewatch = function(obj) { + var m = metaFor(obj, false), chains = m.chains; + + // make sure the object has its own guid. + if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) { + generateGuid(obj, 'ember'); + } + + // make sure any chained watchers update. + if (chains && chains.value() !== obj) { + m.chains = chains.copy(obj); + } +}; + +var NODE_STACK = []; + +/** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. + + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} +*/ +Ember.destroy = function (obj) { + var meta = obj[META_KEY], node, nodes, key, nodeObject; + if (meta) { + obj[META_KEY] = null; + // remove chainWatchers to remove circular references that would prevent GC + node = meta.chains; + if (node) { + NODE_STACK.push(node); + // process tree + while (NODE_STACK.length > 0) { + node = NODE_STACK.pop(); + // push children + nodes = node._chains; + if (nodes) { + for (key in nodes) { + if (nodes.hasOwnProperty(key)) { + NODE_STACK.push(nodes[key]); + } + } + } + // remove chainWatcher in node object + if (node._watching) { + nodeObject = node._object; + if (nodeObject) { + removeChainWatcher(nodeObject, node._key, node); + } + } + } + } + } +}; + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false); + + +var get = Ember.get, + set = Ember.set, + metaFor = Ember.meta, + a_slice = [].slice, + o_create = Ember.create, + META_KEY = Ember.META_KEY, + watch = Ember.watch, + unwatch = Ember.unwatch; + +// .......................................................... +// DEPENDENT KEYS +// + +// data structure: +// meta.deps = { +// 'depKey': { +// 'keyName': count, +// } +// } + +/* + This function returns a map of unique dependencies for a + given object and key. +*/ +function keysForDep(depsMeta, depKey) { + var keys = depsMeta[depKey]; + if (!keys) { + // if there are no dependencies yet for a the given key + // create a new empty list of dependencies for the key + keys = depsMeta[depKey] = {}; + } else if (!depsMeta.hasOwnProperty(depKey)) { + // otherwise if the dependency list is inherited from + // a superclass, clone the hash + keys = depsMeta[depKey] = o_create(keys); + } + return keys; +} + +function metaForDeps(meta) { + return keysForDep(meta, 'deps'); +} + +function addDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // add all of its dependent keys. + var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; + if (!depKeys) return; + + depsMeta = metaForDeps(meta); + + for(idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Increment the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) + 1; + // Watch the depKey + watch(obj, depKey); + } +} + +function removeDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // add all of its dependent keys. + var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; + if (!depKeys) return; + + depsMeta = metaForDeps(meta); + + for(idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Increment the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) - 1; + // Watch the depKey + unwatch(obj, depKey); + } +} + +// .......................................................... +// COMPUTED PROPERTY +// + +/** + @class ComputedProperty + @namespace Ember + @extends Ember.Descriptor + @constructor +*/ +function ComputedProperty(func, opts) { + this.func = func; + + this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; + this._dependentKeys = opts && opts.dependentKeys; + this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly); +} + +Ember.ComputedProperty = ComputedProperty; +ComputedProperty.prototype = new Ember.Descriptor(); + +var ComputedPropertyPrototype = ComputedProperty.prototype; + +/* + Properties are cacheable by default. Computed property will automatically + cache the return value of your function until one of the dependent keys changes. + + Call `volatile()` to set it into non-cached mode. When in this mode + the computed property will not automatically cache the return value. + + However, if a property is properly observable, there is no reason to disable + caching. + + @method cacheable + @param {Boolean} aFlag optional set to `false` to disable caching + @return {Ember.ComputedProperty} this + @chainable +*/ +ComputedPropertyPrototype.cacheable = function(aFlag) { + this._cacheable = aFlag !== false; + return this; +}; + +/** + Call on a computed property to set it into non-cached mode. When in this + mode the computed property will not automatically cache the return value. + + ```javascript + MyApp.outsideService = Ember.Object.create({ + value: function() { + return OutsideService.getValue(); + }.property().volatile() + }); + ``` + + @method volatile + @return {Ember.ComputedProperty} this + @chainable +*/ +ComputedPropertyPrototype.volatile = function() { + return this.cacheable(false); +}; + +/** + Call on a computed property to set it into read-only mode. When in this + mode the computed property will throw an error when set. + + ```javascript + MyApp.person = Ember.Object.create({ + guid: function() { + return 'guid-guid-guid'; + }.property().readOnly() + }); + + MyApp.person.set('guid', 'new-guid'); // will throw an exception + ``` + + @method readOnly + @return {Ember.ComputedProperty} this + @chainable +*/ +ComputedPropertyPrototype.readOnly = function(readOnly) { + this._readOnly = readOnly === undefined || !!readOnly; + return this; +}; + +/** + Sets the dependent keys on this computed property. Pass any number of + arguments containing key paths that this computed property depends on. + + ```javascript + MyApp.president = Ember.Object.create({ + fullName: Ember.computed(function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // Tell Ember that this computed property depends on firstName + // and lastName + }).property('firstName', 'lastName') + }); + ``` + + @method property + @param {String} path* zero or more property paths + @return {Ember.ComputedProperty} this + @chainable +*/ +ComputedPropertyPrototype.property = function() { + var args = []; + for (var i = 0, l = arguments.length; i < l; i++) { + args.push(arguments[i]); + } + this._dependentKeys = args; + return this; +}; + +/** + In some cases, you may want to annotate computed properties with additional + metadata about how they function or what values they operate on. For example, + computed property functions may close over variables that are then no longer + available for introspection. + + You can pass a hash of these values to a computed property like this: + + ``` + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` + + The hash that you pass to the `meta()` function will be saved on the + computed property descriptor under the `_meta` key. Ember runtime + exposes a public API for retrieving these values from classes, + via the `metaForProperty()` function. + + @method meta + @param {Hash} meta + @chainable +*/ + +ComputedPropertyPrototype.meta = function(meta) { + if (arguments.length === 0) { + return this._meta || {}; + } else { + this._meta = meta; + return this; + } +}; + +/* impl descriptor API */ +ComputedPropertyPrototype.willWatch = function(obj, keyName) { + // watch already creates meta for this instance + var meta = obj[META_KEY]; + Ember.assert('watch should have setup meta to be writable', meta.source === obj); + if (!(keyName in meta.cache)) { + addDependentKeys(this, obj, keyName, meta); + } +}; + +ComputedPropertyPrototype.didUnwatch = function(obj, keyName) { + var meta = obj[META_KEY]; + Ember.assert('unwatch should have setup meta to be writable', meta.source === obj); + if (!(keyName in meta.cache)) { + // unwatch already creates meta for this instance + removeDependentKeys(this, obj, keyName, meta); + } +}; + +/* impl descriptor API */ +ComputedPropertyPrototype.didChange = function(obj, keyName) { + // _suspended is set via a CP.set to ensure we don't clear + // the cached value set by the setter + if (this._cacheable && this._suspended !== obj) { + var meta = metaFor(obj); + if (keyName in meta.cache) { + delete meta.cache[keyName]; + if (!meta.watching[keyName]) { + removeDependentKeys(this, obj, keyName, meta); + } + } + } +}; + +/* impl descriptor API */ +ComputedPropertyPrototype.get = function(obj, keyName) { + var ret, cache, meta; + if (this._cacheable) { + meta = metaFor(obj); + cache = meta.cache; + if (keyName in cache) { return cache[keyName]; } + ret = cache[keyName] = this.func.call(obj, keyName); + if (!meta.watching[keyName]) { + addDependentKeys(this, obj, keyName, meta); + } + } else { + ret = this.func.call(obj, keyName); + } + return ret; +}; + +/* impl descriptor API */ +ComputedPropertyPrototype.set = function(obj, keyName, value) { + var cacheable = this._cacheable, + func = this.func, + meta = metaFor(obj, cacheable), + watched = meta.watching[keyName], + oldSuspended = this._suspended, + hadCachedValue = false, + cache = meta.cache, + cachedValue, ret; + + if (this._readOnly) { + throw new Error('Cannot Set: ' + keyName + ' on: ' + obj.toString() ); + } + + this._suspended = obj; + + try { + + if (cacheable && cache.hasOwnProperty(keyName)) { + cachedValue = cache[keyName]; + hadCachedValue = true; + } + + // Check if the CP has been wrapped + if (func.wrappedFunction) { func = func.wrappedFunction; } + + // For backwards-compatibility with computed properties + // that check for arguments.length === 2 to determine if + // they are being get or set, only pass the old cached + // value if the computed property opts into a third + // argument. + if (func.length === 3) { + ret = func.call(obj, keyName, value, cachedValue); + } else if (func.length === 2) { + ret = func.call(obj, keyName, value); + } else { + Ember.defineProperty(obj, keyName, null, cachedValue); + Ember.set(obj, keyName, value); + return; + } + + if (hadCachedValue && cachedValue === ret) { return; } + + if (watched) { Ember.propertyWillChange(obj, keyName); } + + if (hadCachedValue) { + delete cache[keyName]; + } + + if (cacheable) { + if (!watched && !hadCachedValue) { + addDependentKeys(this, obj, keyName, meta); + } + cache[keyName] = ret; + } + + if (watched) { Ember.propertyDidChange(obj, keyName); } + } finally { + this._suspended = oldSuspended; + } + return ret; +}; + +/* called when property is defined */ +ComputedPropertyPrototype.setup = function(obj, keyName) { + var meta = obj[META_KEY]; + if (meta && meta.watching[keyName]) { + addDependentKeys(this, obj, keyName, metaFor(obj)); + } +}; + +/* called before property is overridden */ +ComputedPropertyPrototype.teardown = function(obj, keyName) { + var meta = metaFor(obj); + + if (meta.watching[keyName] || keyName in meta.cache) { + removeDependentKeys(this, obj, keyName, meta); + } + + if (this._cacheable) { delete meta.cache[keyName]; } + + return null; // no value to restore +}; + + +/** + This helper returns a new property descriptor that wraps the passed + computed property function. You can use this helper to define properties + with mixins or via `Ember.defineProperty()`. + + The function you pass will be used to both get and set property values. + The function should accept two parameters, key and value. If value is not + undefined you should set the value first. In either case return the + current value of the property. + @method computed + @for Ember + @param {Function} func The computed property function. + @return {Ember.ComputedProperty} property descriptor instance +*/ +Ember.computed = function(func) { + var args; + + if (arguments.length > 1) { + args = a_slice.call(arguments, 0, -1); + func = a_slice.call(arguments, -1)[0]; + } + + if ( typeof func !== "function" ) { + throw new Error("Computed Property declared without a property function"); + } + + var cp = new ComputedProperty(func); + + if (args) { + cp.property.apply(cp, args); + } + + return cp; +}; + +/** + Returns the cached value for a property, if one exists. + This can be useful for peeking at the value of a computed + property that is generated lazily, without accidentally causing + it to be created. + + @method cacheFor + @for Ember + @param {Object} obj the object whose property you want to check + @param {String} key the name of the property whose cached value you want + to return + @return {*} the cached value +*/ +Ember.cacheFor = function cacheFor(obj, key) { + var cache = metaFor(obj, false).cache; + + if (cache && key in cache) { + return cache[key]; + } +}; + +function getProperties(self, propertyNames) { + var ret = {}; + for(var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = get(self, propertyNames[i]); + } + return ret; +} + +function registerComputed(name, macro) { + Ember.computed[name] = function(dependentKey) { + var args = a_slice.call(arguments); + return Ember.computed(dependentKey, function() { + return macro.apply(this, args); + }); + }; +} + +function registerComputedWithProperties(name, macro) { + Ember.computed[name] = function() { + var properties = a_slice.call(arguments); + + var computed = Ember.computed(function() { + return macro.apply(this, [getProperties(this, properties)]); + }); + + return computed.property.apply(computed, properties); + }; +} + +/** + @method computed.empty + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which negate + the original value for property +*/ +registerComputed('empty', function(dependentKey) { + return Ember.isEmpty(get(this, dependentKey)); +}); + +/** + @method computed.notEmpty + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. +*/ +registerComputed('notEmpty', function(dependentKey) { + return !Ember.isEmpty(get(this, dependentKey)); +}); + +/** + @method computed.none + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + rturns true if original value for property is null or undefined. +*/ +registerComputed('none', function(dependentKey) { + return Ember.isNone(get(this, dependentKey)); +}); + +/** + @method computed.not + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property +*/ +registerComputed('not', function(dependentKey) { + return !get(this, dependentKey); +}); + +/** + @method computed.bool + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which convert + to boolean the original value for property +*/ +registerComputed('bool', function(dependentKey) { + return !!get(this, dependentKey); +}); + +/** + @method computed.match + @for Ember + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp +*/ +registerComputed('match', function(dependentKey, regexp) { + var value = get(this, dependentKey); + return typeof value === 'string' ? !!value.match(regexp) : false; +}); + +/** + @method computed.equal + @for Ember + @param {String} dependentKey + @param {String|Number|Object} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is equal to the given value. +*/ +registerComputed('equal', function(dependentKey, value) { + return get(this, dependentKey) === value; +}); + +/** + @method computed.gt + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater then given value. +*/ +registerComputed('gt', function(dependentKey, value) { + return get(this, dependentKey) > value; +}); + +/** + @method computed.gte + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater or equal then given value. +*/ +registerComputed('gte', function(dependentKey, value) { + return get(this, dependentKey) >= value; +}); + +/** + @method computed.lt + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less then given value. +*/ +registerComputed('lt', function(dependentKey, value) { + return get(this, dependentKey) < value; +}); + +/** + @method computed.lte + @for Ember + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal then given value. +*/ +registerComputed('lte', function(dependentKey, value) { + return get(this, dependentKey) <= value; +}); + +/** + @method computed.and + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which peforms + a logical `and` on the values of all the original values for properties. +*/ +registerComputedWithProperties('and', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && !properties[key]) { + return false; + } + } + return true; +}); + +/** + @method computed.or + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which peforms + a logical `or` on the values of all the original values for properties. +*/ +registerComputedWithProperties('or', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return true; + } + } + return false; +}); + +/** + @method computed.any + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which returns + the first truthy value of given list of properties. +*/ +registerComputedWithProperties('any', function(properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return null; +}); + +/** + @method computed.map + @for Ember + @param {String} dependentKey, [dependentKey...] + @return {Ember.ComputedProperty} computed property which maps + values of all passed properties in to an array. +*/ +registerComputedWithProperties('map', function(properties) { + var res = []; + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + if (Ember.isNone(properties[key])) { + res.push(null); + } else { + res.push(properties[key]); + } + } + } + return res; +}); + +/** + @method computed.alias + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias to the original value for property. +*/ +Ember.computed.alias = function(dependentKey) { + return Ember.computed(dependentKey, function(key, value){ + if (arguments.length > 1) { + set(this, dependentKey, value); + return value; + } else { + return get(this, dependentKey); + } + }); +}; + +/** + @method computed.oneWay + @for Ember + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + one way computed property to the original value for property. + + Where `computed.alias` aliases `get` and `set`, and allows for bidirectional + data flow, `computed.oneWay` only provides an aliased `get`. The `set` will + not mutate the upstream property, rather causes the current property to + become the value set. This causes the downstream property to permentantly + diverge from the upstream property. + + ```javascript + User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.oneWay('firstName') + }); + + user = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); + + user.get('nickName'); + # 'Teddy' + + user.set('nickName', 'TeddyBear'); + # 'TeddyBear' + + user.get('firstName'); + # 'Teddy' + ``` +*/ +Ember.computed.oneWay = function(dependentKey) { + return Ember.computed(dependentKey, function() { + return get(this, dependentKey); + }); +}; + + +/** + @method computed.defaultTo + @for Ember + @param {String} defaultPath + @return {Ember.ComputedProperty} computed property which acts like + a standard getter and setter, but defaults to the value from `defaultPath`. +*/ +Ember.computed.defaultTo = function(defaultPath) { + return Ember.computed(function(key, newValue, cachedValue) { + if (arguments.length === 1) { + return cachedValue != null ? cachedValue : get(this, defaultPath); + } + return newValue != null ? newValue : get(this, defaultPath); + }); +}; + +})(); + + + +(function() { +// Ember.tryFinally +/** +@module ember-metal +*/ + +var AFTER_OBSERVERS = ':change'; +var BEFORE_OBSERVERS = ':before'; + +function changeEvent(keyName) { + return keyName+AFTER_OBSERVERS; +} + +function beforeEvent(keyName) { + return keyName+BEFORE_OBSERVERS; +} + +/** + @method addObserver + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] +*/ +Ember.addObserver = function(obj, path, target, method) { + Ember.addListener(obj, changeEvent(path), target, method); + Ember.watch(obj, path); + return this; +}; + +Ember.observersFor = function(obj, path) { + return Ember.listenersFor(obj, changeEvent(path)); +}; + +/** + @method removeObserver + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] +*/ +Ember.removeObserver = function(obj, path, target, method) { + Ember.unwatch(obj, path); + Ember.removeListener(obj, changeEvent(path), target, method); + return this; +}; + +/** + @method addBeforeObserver + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] +*/ +Ember.addBeforeObserver = function(obj, path, target, method) { + Ember.addListener(obj, beforeEvent(path), target, method); + Ember.watch(obj, path); + return this; +}; + +// Suspend observer during callback. +// +// This should only be used by the target of the observer +// while it is setting the observed path. +Ember._suspendBeforeObserver = function(obj, path, target, method, callback) { + return Ember._suspendListener(obj, beforeEvent(path), target, method, callback); +}; + +Ember._suspendObserver = function(obj, path, target, method, callback) { + return Ember._suspendListener(obj, changeEvent(path), target, method, callback); +}; + +var map = Ember.ArrayPolyfills.map; + +Ember._suspendBeforeObservers = function(obj, paths, target, method, callback) { + var events = map.call(paths, beforeEvent); + return Ember._suspendListeners(obj, events, target, method, callback); +}; + +Ember._suspendObservers = function(obj, paths, target, method, callback) { + var events = map.call(paths, changeEvent); + return Ember._suspendListeners(obj, events, target, method, callback); +}; + +Ember.beforeObserversFor = function(obj, path) { + return Ember.listenersFor(obj, beforeEvent(path)); +}; + +/** + @method removeBeforeObserver + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] +*/ +Ember.removeBeforeObserver = function(obj, path, target, method) { + Ember.unwatch(obj, path); + Ember.removeListener(obj, beforeEvent(path), target, method); + return this; +}; +})(); + + + +(function() { +define("backburner", + ["backburner/deferred_action_queues","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var DeferredActionQueues = __dependency1__.DeferredActionQueues; + + var slice = [].slice, + pop = [].pop, + debouncees = [], + timers = [], + autorun, laterTimer, laterTimerExpiresAt; + + function Backburner(queueNames, options) { + this.queueNames = queueNames; + this.options = options || {}; + if (!this.options.defaultQueue) { + this.options.defaultQueue = queueNames[0]; + } + this.instanceStack = []; + } + + Backburner.prototype = { + queueNames: null, + options: null, + currentInstance: null, + instanceStack: null, + + begin: function() { + var onBegin = this.options && this.options.onBegin, + previousInstance = this.currentInstance; + + if (previousInstance) { + this.instanceStack.push(previousInstance); + } + + this.currentInstance = new DeferredActionQueues(this.queueNames, this.options); + if (onBegin) { + onBegin(this.currentInstance, previousInstance); + } + }, + + end: function() { + var onEnd = this.options && this.options.onEnd, + currentInstance = this.currentInstance, + nextInstance = null; + + try { + currentInstance.flush(); + } finally { + this.currentInstance = null; + + if (this.instanceStack.length) { + nextInstance = this.instanceStack.pop(); + this.currentInstance = nextInstance; + } + + if (onEnd) { + onEnd(currentInstance, nextInstance); + } + } + }, + + run: function(target, method /*, args */) { + var ret; + this.begin(); + + if (!method) { + method = target; + target = null; + } + + if (typeof method === 'string') { + method = target[method]; + } + + // Prevent Safari double-finally. + var finallyAlreadyCalled = false; + try { + if (arguments.length > 2) { + ret = method.apply(target, slice.call(arguments, 2)); + } else { + ret = method.call(target); + } + } finally { + if (!finallyAlreadyCalled) { + finallyAlreadyCalled = true; + this.end(); + } + } + return ret; + }, + + defer: function(queueName, target, method /* , args */) { + if (!method) { + method = target; + target = null; + } + + if (typeof method === 'string') { + method = target[method]; + } + + var stack = this.DEBUG ? new Error().stack : undefined, + args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; + if (!this.currentInstance) { createAutorun(this); } + return this.currentInstance.schedule(queueName, target, method, args, false, stack); + }, + + deferOnce: function(queueName, target, method /* , args */) { + if (!method) { + method = target; + target = null; + } + + if (typeof method === 'string') { + method = target[method]; + } + + var stack = this.DEBUG ? new Error().stack : undefined, + args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; + if (!this.currentInstance) { createAutorun(this); } + return this.currentInstance.schedule(queueName, target, method, args, true, stack); + }, + + setTimeout: function() { + var self = this, + wait = pop.call(arguments), + target = arguments[0], + method = arguments[1], + executeAt = (+new Date()) + wait; + + if (!method) { + method = target; + target = null; + } + + if (typeof method === 'string') { + method = target[method]; + } + + var fn, args; + if (arguments.length > 2) { + args = slice.call(arguments, 2); + + fn = function() { + method.apply(target, args); + }; + } else { + fn = function() { + method.call(target); + }; + } + + // find position to insert - TODO: binary search + var i, l; + for (i = 0, l = timers.length; i < l; i += 2) { + if (executeAt < timers[i]) { break; } + } + + timers.splice(i, 0, executeAt, fn); + + if (laterTimer && laterTimerExpiresAt < executeAt) { return fn; } + + if (laterTimer) { + clearTimeout(laterTimer); + laterTimer = null; + } + laterTimer = window.setTimeout(function() { + executeTimers(self); + laterTimer = null; + laterTimerExpiresAt = null; + }, wait); + laterTimerExpiresAt = executeAt; + + return fn; + }, + + debounce: function(target, method /* , args, wait */) { + var self = this, + args = arguments, + wait = pop.call(args), + debouncee; + + for (var i = 0, l = debouncees.length; i < l; i++) { + debouncee = debouncees[i]; + if (debouncee[0] === target && debouncee[1] === method) { return; } // do nothing + } + + var timer = window.setTimeout(function() { + self.run.apply(self, args); + + // remove debouncee + var index = -1; + for (var i = 0, l = debouncees.length; i < l; i++) { + debouncee = debouncees[i]; + if (debouncee[0] === target && debouncee[1] === method) { + index = i; + break; + } + } + + if (index > -1) { debouncees.splice(index, 1); } + }, wait); + + debouncees.push([target, method, timer]); + }, + + cancelTimers: function() { + for (var i = 0, l = debouncees.length; i < l; i++) { + clearTimeout(debouncees[i][2]); + } + debouncees = []; + + if (laterTimer) { + clearTimeout(laterTimer); + laterTimer = null; + } + timers = []; + + if (autorun) { + clearTimeout(autorun); + autorun = null; + } + }, + + hasTimers: function() { + return !!timers.length || autorun; + }, + + cancel: function(timer) { + if (timer && typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce + return timer.queue.cancel(timer); + } else if (typeof timer === 'function') { // we're cancelling a setTimeout + for (var i = 0, l = timers.length; i < l; i += 2) { + if (timers[i + 1] === timer) { + timers.splice(i, 2); // remove the two elements + return true; + } + } + } else { + return; // timer was null or not a timer + } + } + }; + + Backburner.prototype.schedule = Backburner.prototype.defer; + Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; + Backburner.prototype.later = Backburner.prototype.setTimeout; + + function createAutorun(backburner) { + backburner.begin(); + autorun = window.setTimeout(function() { + backburner.end(); + autorun = null; + }); + } + + function executeTimers(self) { + var now = +new Date(), + time, fns, i, l; + + self.run(function() { + // TODO: binary search + for (i = 0, l = timers.length; i < l; i += 2) { + time = timers[i]; + if (time > now) { break; } + } + + fns = timers.splice(0, i); + + for (i = 1, l = fns.length; i < l; i += 2) { + self.schedule(self.options.defaultQueue, null, fns[i]); + } + }); + + if (timers.length) { + laterTimer = window.setTimeout(function() { + executeTimers(self); + laterTimer = null; + laterTimerExpiresAt = null; + }, timers[0] - now); + laterTimerExpiresAt = timers[0]; + } + } + + + __exports__.Backburner = Backburner; + }); + +define("backburner/deferred_action_queues", + ["backburner/queue","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Queue = __dependency1__.Queue; + + function DeferredActionQueues(queueNames, options) { + var queues = this.queues = {}; + this.queueNames = queueNames = queueNames || []; + + var queueName; + for (var i = 0, l = queueNames.length; i < l; i++) { + queueName = queueNames[i]; + queues[queueName] = new Queue(this, queueName, options[queueName]); + } + } + + DeferredActionQueues.prototype = { + queueNames: null, + queues: null, + + schedule: function(queueName, target, method, args, onceFlag, stack) { + var queues = this.queues, + queue = queues[queueName]; + + if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } + + if (onceFlag) { + return queue.pushUnique(target, method, args, stack); + } else { + return queue.push(target, method, args, stack); + } + }, + + flush: function() { + var queues = this.queues, + queueNames = this.queueNames, + queueName, queue, queueItems, priorQueueNameIndex, + queueNameIndex = 0, numberOfQueues = queueNames.length; + + outerloop: + while (queueNameIndex < numberOfQueues) { + queueName = queueNames[queueNameIndex]; + queue = queues[queueName]; + queueItems = queue._queueBeingFlushed = queue._queue.slice(); + queue._queue = []; + + var options = queue.options, + before = options && options.before, + after = options && options.after, + target, method, args, stack, + queueIndex = 0, numberOfQueueItems = queueItems.length; + + if (numberOfQueueItems && before) { before(); } + while (queueIndex < numberOfQueueItems) { + target = queueItems[queueIndex]; + method = queueItems[queueIndex+1]; + args = queueItems[queueIndex+2]; + stack = queueItems[queueIndex+3]; // Debugging assistance + + if (typeof method === 'string') { method = target[method]; } + + // method could have been nullified / canceled during flush + if (method) { + // TODO: error handling + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + } + + queueIndex += 4; + } + queue._queueBeingFlushed = null; + if (numberOfQueueItems && after) { after(); } + + if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { + queueNameIndex = priorQueueNameIndex; + continue outerloop; + } + + queueNameIndex++; + } + } + }; + + function indexOfPriorQueueWithActions(daq, currentQueueIndex) { + var queueName, queue; + + for (var i = 0, l = currentQueueIndex; i <= l; i++) { + queueName = daq.queueNames[i]; + queue = daq.queues[queueName]; + if (queue._queue.length) { return i; } + } + + return -1; + } + + + __exports__.DeferredActionQueues = DeferredActionQueues; + }); + +define("backburner/queue", + ["exports"], + function(__exports__) { + "use strict"; + function Queue(daq, name, options) { + this.daq = daq; + this.name = name; + this.options = options; + this._queue = []; + } + + Queue.prototype = { + daq: null, + name: null, + options: null, + _queue: null, + + push: function(target, method, args, stack) { + var queue = this._queue; + queue.push(target, method, args, stack); + return {queue: this, target: target, method: method}; + }, + + pushUnique: function(target, method, args, stack) { + var queue = this._queue, currentTarget, currentMethod, i, l; + + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; + + if (currentTarget === target && currentMethod === method) { + queue[i+2] = args; // replace args + queue[i+3] = stack; // replace stack + return {queue: this, target: target, method: method}; // TODO: test this code path + } + } + + this._queue.push(target, method, args, stack); + return {queue: this, target: target, method: method}; + }, + + // TODO: remove me, only being used for Ember.run.sync + flush: function() { + var queue = this._queue, + options = this.options, + before = options && options.before, + after = options && options.after, + target, method, args, stack, i, l = queue.length; + + if (l && before) { before(); } + for (i = 0; i < l; i += 4) { + target = queue[i]; + method = queue[i+1]; + args = queue[i+2]; + stack = queue[i+3]; // Debugging assistance + + // TODO: error handling + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + } + if (l && after) { after(); } + + // check if new items have been added + if (queue.length > l) { + this._queue = queue.slice(l); + this.flush(); + } else { + this._queue.length = 0; + } + }, + + cancel: function(actionToCancel) { + var queue = this._queue, currentTarget, currentMethod, i, l; + + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; + + if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + queue.splice(i, 4); + return true; + } + } + + // if not found in current queue + // could be in the queue that is being flushed + queue = this._queueBeingFlushed; + if (!queue) { + return; + } + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; + + if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + // don't mess with array during flush + // just nullify the method + queue[i+1] = null; + return true; + } + } + } + }; + + + __exports__.Queue = Queue; + }); +})(); + + + +(function() { +var onBegin = function(current) { + Ember.run.currentRunLoop = current; +}; + +var onEnd = function(current, next) { + Ember.run.currentRunLoop = next; +}; + +var Backburner = requireModule('backburner').Backburner, + backburner = new Backburner(['sync', 'actions', 'destroy'], { + sync: { + before: Ember.beginPropertyChanges, + after: Ember.endPropertyChanges + }, + defaultQueue: 'actions', + onBegin: onBegin, + onEnd: onEnd + }), + slice = [].slice; + +// .......................................................... +// Ember.run - this is ideally the only public API the dev sees +// + +/** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. + + Normally you should not need to invoke this method yourself. However if + you are implementing raw event handlers when interfacing with other + libraries or plugins, you should probably wrap all of your code inside this + call. + + ```javascript + Ember.run(function(){ + // code to be execute within a RunLoop + }); + ``` + + @class run + @namespace Ember + @static + @constructor + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. +*/ +Ember.run = function(target, method) { + var ret; + + if (Ember.onerror) { + try { + ret = backburner.run.apply(backburner, arguments); + } catch (e) { + Ember.onerror(e); + } + } else { + ret = backburner.run.apply(backburner, arguments); + } + + return ret; +}; + +/** + + If no run-loop is present, it creates a new one. If a run loop is + present it will queue itself to run on the existing run-loops action + queue. + + Please note: This is not for normal usage, and should be used sparingly. + + If invoked when not within a run loop: + + ```javascript + Ember.run.join(function(){ + // creates a new run-loop + }); + ``` + + Alternatively, if called within an existing run loop: + + ```javascript + Ember.run(function(){ + // creates a new run-loop + Ember.run.join(function(){ + // joins with the existing run-loop, and queues for invocation on + // the existing run-loops action queue. + }); + }); + ``` + + @method join + @namespace Ember + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. Please note, + when called within an existing loop, no return value is possible. +*/ +Ember.run.join = function(target, method) { + if (!Ember.run.currentRunLoop) { + return Ember.run.apply(Ember.run, arguments); + } + + var args = slice.call(arguments); + args.unshift('actions'); + Ember.run.schedule.apply(Ember.run, args); +}; + +Ember.run.backburner = backburner; + +var run = Ember.run; + +Ember.run.currentRunLoop = null; + +Ember.run.queues = backburner.queueNames; + +/** + Begins a new RunLoop. Any deferred actions invoked after the begin will + be buffered until you invoke a matching call to `Ember.run.end()`. This is + a lower-level way to use a RunLoop instead of using `Ember.run()`. + + ```javascript + Ember.run.begin(); + // code to be execute within a RunLoop + Ember.run.end(); + ``` + + @method begin + @return {void} +*/ +Ember.run.begin = function() { + backburner.begin(); +}; + +/** + Ends a RunLoop. This must be called sometime after you call + `Ember.run.begin()` to flush any deferred actions. This is a lower-level way + to use a RunLoop instead of using `Ember.run()`. + + ```javascript + Ember.run.begin(); + // code to be execute within a RunLoop + Ember.run.end(); + ``` + + @method end + @return {void} +*/ +Ember.run.end = function() { + backburner.end(); +}; + +/** + Array of named queues. This array determines the order in which queues + are flushed at the end of the RunLoop. You can define your own queues by + simply adding the queue name to this array. Normally you should not need + to inspect or modify this property. + + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] +*/ + +/** + Adds the passed target/method and any optional arguments to the named + queue to be executed at the end of the RunLoop. If you have not already + started a RunLoop when calling this method one will be started for you + automatically. + + At the end of a RunLoop, any methods scheduled in this way will be invoked. + Methods will be invoked in an order matching the named queues defined in + the `Ember.run.queues` property. + + ```javascript + Ember.run.schedule('sync', this, function(){ + // this will be executed in the first RunLoop queue, when bindings are synced + console.log("scheduled on sync queue"); + }); + + Ember.run.schedule('actions', this, function(){ + // this will be executed in the 'actions' queue, after bindings have synced. + console.log("scheduled on actions queue"); + }); + + // Note the functions will be run in order based on the run queues order. Output would be: + // scheduled on sync queue + // scheduled on actions queue + ``` + + @method schedule + @param {String} queue The name of the queue to schedule against. + Default queues are 'sync' and 'actions' + @param {Object} [target] target object to use as the context when invoking a method. + @param {String|Function} method The method to invoke. If you pass a string it + will be resolved on the target object at the time the scheduled item is + invoked allowing you to change the target function. + @param {Object} [arguments*] Optional arguments to be passed to the queued method. + @return {void} +*/ +Ember.run.schedule = function(queue, target, method) { + checkAutoRun(); + backburner.schedule.apply(backburner, arguments); +}; + +// Used by global test teardown +Ember.run.hasScheduledTimers = function() { + return backburner.hasTimers(); +}; + +// Used by global test teardown +Ember.run.cancelTimers = function () { + backburner.cancelTimers(); +}; + +/** + Immediately flushes any events scheduled in the 'sync' queue. Bindings + use this queue so this method is a useful way to immediately force all + bindings in the application to sync. + + You should call this method anytime you need any changed state to propagate + throughout the app immediately without repainting the UI (which happens + in the later 'render' queue added by the `ember-views` package). + + ```javascript + Ember.run.sync(); + ``` + + @method sync + @return {void} +*/ +Ember.run.sync = function() { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); + } +}; + +/** + Invokes the passed target/method and optional arguments after a specified + period if time. The last parameter of this method must always be a number + of milliseconds. + + You should use this method whenever you need to run some action after a + period of time instead of using `setTimeout()`. This method will ensure that + items that expire during the same script execution cycle all execute + together, which is often more efficient than using a real setTimeout. + + ```javascript + Ember.run.later(myContext, function(){ + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); + ``` + + @method later + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @return {String} a string you can use to cancel the timer in + `Ember.run.cancel` later. +*/ +Ember.run.later = function(target, method) { + return backburner.later.apply(backburner, arguments); +}; + +/** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. + + @method once + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} timer +*/ +Ember.run.once = function(target, method) { + checkAutoRun(); + var args = slice.call(arguments); + args.unshift('actions'); + return backburner.scheduleOnce.apply(backburner, args); +}; + +/** + Schedules a function to run one time in a given queue of the current RunLoop. + Calling this method with the same queue/target/method combination will have + no effect (past the initial call). + + Note that although you can pass optional arguments these will not be + considered when looking for duplicates. New arguments will replace previous + calls. + + ```javascript + Ember.run(function(){ + var sayHi = function() { console.log('hi'); } + Ember.run.scheduleOnce('afterRender', myContext, sayHi); + Ember.run.scheduleOnce('afterRender', myContext, sayHi); + // doFoo will only be executed once, in the afterRender queue of the RunLoop + }); + ``` + + Also note that passing an anonymous function to `Ember.run.scheduleOnce` will + not prevent additional calls with an identical anonymous function from + scheduling the items multiple times, e.g.: + + ```javascript + function scheduleIt() { + Ember.run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); + } + scheduleIt(); + scheduleIt(); + // "Closure" will print twice, even though we're using `Ember.run.scheduleOnce`, + // because the function we pass to it is anonymous and won't match the + // previously scheduled operation. + ``` + + Available queues, and their order, can be found at `Ember.run.queues` + + @method scheduleOnce + @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} timer +*/ +Ember.run.scheduleOnce = function(queue, target, method) { + checkAutoRun(); + return backburner.scheduleOnce.apply(backburner, arguments); +}; + +/** + Schedules an item to run from within a separate run loop, after + control has been returned to the system. This is equivalent to calling + `Ember.run.later` with a wait time of 1ms. + + ```javascript + Ember.run.next(myContext, function(){ + // code to be executed in the next run loop, which will be scheduled after the current one + }); + ``` + + Multiple operations scheduled with `Ember.run.next` will coalesce + into the same later run loop, along with any other operations + scheduled by `Ember.run.later` that expire right around the same + time that `Ember.run.next` operations will fire. + + Note that there are often alternatives to using `Ember.run.next`. + For instance, if you'd like to schedule an operation to happen + after all DOM element operations have completed within the current + run loop, you can make use of the `afterRender` run loop queue (added + by the `ember-views` package, along with the preceding `render` queue + where all the DOM element operations happen). Example: + + ```javascript + App.MyCollectionView = Ember.CollectionView.extend({ + didInsertElement: function() { + Ember.run.scheduleOnce('afterRender', this, 'processChildElements'); + }, + processChildElements: function() { + // ... do something with collectionView's child view + // elements after they've finished rendering, which + // can't be done within the CollectionView's + // `didInsertElement` hook because that gets run + // before the child elements have been added to the DOM. + } + }); + ``` + + One benefit of the above approach compared to using `Ember.run.next` is + that you will be able to perform DOM/CSS operations before unprocessed + elements are rendered to the screen, which may prevent flickering or + other artifacts caused by delaying processing until after rendering. + + The other major benefit to the above approach is that `Ember.run.next` + introduces an element of non-determinism, which can make things much + harder to test, due to its reliance on `setTimeout`; it's much harder + to guarantee the order of scheduled operations when they are scheduled + outside of the current run loop, i.e. with `Ember.run.next`. + + @method next + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} timer +*/ +Ember.run.next = function() { + var args = slice.call(arguments); + args.push(1); + return backburner.later.apply(backburner, args); +}; + +/** + Cancels a scheduled item. Must be a value returned by `Ember.run.later()`, + `Ember.run.once()`, or `Ember.run.next()`. + + ```javascript + var runNext = Ember.run.next(myContext, function(){ + // will not be executed + }); + Ember.run.cancel(runNext); + + var runLater = Ember.run.later(myContext, function(){ + // will not be executed + }, 500); + Ember.run.cancel(runLater); + + var runOnce = Ember.run.once(myContext, function(){ + // will not be executed + }); + Ember.run.cancel(runOnce); + ``` + + @method cancel + @param {Object} timer Timer object to cancel + @return {void} +*/ +Ember.run.cancel = function(timer) { + return backburner.cancel(timer); +}; + +/** + Execute the passed method in a specified amount of time, reset timer + upon additional calls. + + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; + + Ember.run.debounce(myContext, myFunc, 150); + + // less than 150ms passes + + Ember.run.debounce(myContext, myFunc, 150); + + // 150ms passes + // myFunc is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` + + @method debounce + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @return {void} +*/ +Ember.run.debounce = function() { + return backburner.debounce.apply(backburner, arguments); +}; + +// Make sure it's not an autorun during testing +function checkAutoRun() { + if (!Ember.run.currentRunLoop) { + Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run", !Ember.testing); + } +} + +})(); + + + +(function() { +// Ember.Logger +// get +// set +// guidFor, meta +// addObserver, removeObserver +// Ember.run.schedule +/** +@module ember-metal +*/ + +// .......................................................... +// CONSTANTS +// + +/** + Debug parameter you can turn on. This will log all bindings that fire to + the console. This should be disabled in production code. Note that you + can also enable this from the console or temporarily. + + @property LOG_BINDINGS + @for Ember + @type Boolean + @default false +*/ +Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; + +var get = Ember.get, + set = Ember.set, + guidFor = Ember.guidFor, + IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; + +/** + Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) + instead of local (`foo.bar.baz`). + + @method isGlobalPath + @for Ember + @private + @param {String} path + @return Boolean +*/ +var isGlobalPath = Ember.isGlobalPath = function(path) { + return IS_GLOBAL.test(path); +}; + +function getWithGlobals(obj, path) { + return get(isGlobalPath(path) ? Ember.lookup : obj, path); +} + +// .......................................................... +// BINDING +// + +var Binding = function(toPath, fromPath) { + this._direction = 'fwd'; + this._from = fromPath; + this._to = toPath; + this._directionMap = Ember.Map.create(); +}; + +/** +@class Binding +@namespace Ember +*/ + +Binding.prototype = { + /** + This copies the Binding so it can be connected to another object. + + @method copy + @return {Ember.Binding} + */ + copy: function () { + var copy = new Binding(this._to, this._from); + if (this._oneWay) { copy._oneWay = true; } + return copy; + }, + + // .......................................................... + // CONFIG + // + + /** + This will set `from` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. + + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. + + @method from + @param {String} path the property path to connect to + @return {Ember.Binding} `this` + */ + from: function(path) { + this._from = path; + return this; + }, + + /** + This will set the `to` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. + + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. + + @method to + @param {String|Tuple} path A property path or tuple + @return {Ember.Binding} `this` + */ + to: function(path) { + this._to = path; + return this; + }, + + /** + Configures the binding as one way. A one-way binding will relay changes + on the `from` side to the `to` side, but not the other way around. This + means that if you change the `to` side directly, the `from` side may have + a different value. + + @method oneWay + @return {Ember.Binding} `this` + */ + oneWay: function() { + this._oneWay = true; + return this; + }, + + /** + @method toString + @return {String} string representation of binding + */ + toString: function() { + var oneWay = this._oneWay ? '[oneWay]' : ''; + return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; + }, + + // .......................................................... + // CONNECT AND SYNC + // + + /** + Attempts to connect this binding instance so that it can receive and relay + changes. This method will raise an exception if you have not set the + from/to properties yet. + + @method connect + @param {Object} obj The root object for this binding. + @return {Ember.Binding} `this` + */ + connect: function(obj) { + Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj); + + var fromPath = this._from, toPath = this._to; + Ember.trySet(obj, toPath, getWithGlobals(obj, fromPath)); + + // add an observer on the object to be notified when the binding should be updated + Ember.addObserver(obj, fromPath, this, this.fromDidChange); + + // if the binding is a two-way binding, also set up an observer on the target + if (!this._oneWay) { Ember.addObserver(obj, toPath, this, this.toDidChange); } + + this._readyToSync = true; + + return this; + }, + + /** + Disconnects the binding instance. Changes will no longer be relayed. You + will not usually need to call this method. + + @method disconnect + @param {Object} obj The root object you passed when connecting the binding. + @return {Ember.Binding} `this` + */ + disconnect: function(obj) { + Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj); + + var twoWay = !this._oneWay; + + // remove an observer on the object so we're no longer notified of + // changes that should update bindings. + Ember.removeObserver(obj, this._from, this, this.fromDidChange); + + // if the binding is two-way, remove the observer from the target as well + if (twoWay) { Ember.removeObserver(obj, this._to, this, this.toDidChange); } + + this._readyToSync = false; // disable scheduled syncs... + return this; + }, + + // .......................................................... + // PRIVATE + // + + /* called when the from side changes */ + fromDidChange: function(target) { + this._scheduleSync(target, 'fwd'); + }, + + /* called when the to side changes */ + toDidChange: function(target) { + this._scheduleSync(target, 'back'); + }, + + _scheduleSync: function(obj, dir) { + var directionMap = this._directionMap; + var existingDir = directionMap.get(obj); + + // if we haven't scheduled the binding yet, schedule it + if (!existingDir) { + Ember.run.schedule('sync', this, this._sync, obj); + directionMap.set(obj, dir); + } + + // If both a 'back' and 'fwd' sync have been scheduled on the same object, + // default to a 'fwd' sync so that it remains deterministic. + if (existingDir === 'back' && dir === 'fwd') { + directionMap.set(obj, 'fwd'); + } + }, + + _sync: function(obj) { + var log = Ember.LOG_BINDINGS; + + // don't synchronize destroyed objects or disconnected bindings + if (obj.isDestroyed || !this._readyToSync) { return; } + + // get the direction of the binding for the object we are + // synchronizing from + var directionMap = this._directionMap; + var direction = directionMap.get(obj); + + var fromPath = this._from, toPath = this._to; + + directionMap.remove(obj); + + // if we're synchronizing from the remote object... + if (direction === 'fwd') { + var fromValue = getWithGlobals(obj, this._from); + if (log) { + Ember.Logger.log(' ', this.toString(), '->', fromValue, obj); + } + if (this._oneWay) { + Ember.trySet(obj, toPath, fromValue); + } else { + Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () { + Ember.trySet(obj, toPath, fromValue); + }); + } + // if we're synchronizing *to* the remote object + } else if (direction === 'back') { + var toValue = get(obj, this._to); + if (log) { + Ember.Logger.log(' ', this.toString(), '<-', toValue, obj); + } + Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () { + Ember.trySet(Ember.isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue); + }); + } + } + +}; + +function mixinProperties(to, from) { + for (var key in from) { + if (from.hasOwnProperty(key)) { + to[key] = from[key]; + } + } +} + +mixinProperties(Binding, { + + /** + See `Ember.Binding.from`. + + @method from + @static + */ + from: function() { + var C = this, binding = new C(); + return binding.from.apply(binding, arguments); + }, + + /** + See `Ember.Binding.to`. + + @method to + @static + */ + to: function() { + var C = this, binding = new C(); + return binding.to.apply(binding, arguments); + }, + + /** + Creates a new Binding instance and makes it apply in a single direction. + A one-way binding will relay changes on the `from` side object (supplied + as the `from` argument) the `to` side, but not the other way around. + This means that if you change the "to" side directly, the "from" side may have + a different value. + + See `Binding.oneWay`. + + @method oneWay + @param {String} from from path. + @param {Boolean} [flag] (Optional) passing nothing here will make the + binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the + binding two way again. + */ + oneWay: function(from, flag) { + var C = this, binding = new C(null, from); + return binding.oneWay(flag); + } + +}); + +/** + An `Ember.Binding` connects the properties of two objects so that whenever + the value of one property changes, the other property will be changed also. + + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. + + Properties ending in a `Binding` suffix will be converted to `Ember.Binding` + instances. The value of this property should be a string representing a path + to another object or a custom binding instanced created using Binding helpers + (see "One Way Bindings"): + + ``` + valueBinding: "MyApp.someController.title" + ``` + + This will create a binding from `MyApp.someController.title` to the `value` + property of your object instance automatically. Now the two values will be + kept in sync. + + ## One Way Bindings + + One especially useful binding customization you can use is the `oneWay()` + helper. This helper tells Ember that you are only interested in + receiving changes on the object you are binding from. For example, if you + are binding to a preference and you want to be notified if the preference + has changed, but your object will not be changing the preference itself, you + could do: + + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") + ``` + + This way if the value of `MyApp.preferencesController.bigTitles` changes the + `bigTitles` property of your object will change also. However, if you + change the value of your `bigTitles` property, it will not update the + `preferencesController`. + + One way bindings are almost twice as fast to setup and twice as fast to + execute because the binding only has to worry about changes to one side. + + You should consider using one way bindings anytime you have an object that + may be created frequently and you do not intend to change a property; only + to monitor it for changes (such as in the example above). + + ## Adding Bindings Manually + + All of the examples above show you how to configure a custom binding, but the + result of these customizations will be a binding template, not a fully active + Binding instance. The binding will actually become active only when you + instantiate the object the binding belongs to. It is useful however, to + understand what actually happens when the binding is activated. + + For a binding to function it must have at least a `from` property and a `to` + property. The `from` property path points to the object/key that you want to + bind from while the `to` path points to the object/key you want to bind to. + + When you define a custom binding, you are usually describing the property + you want to bind from (such as `MyApp.someController.value` in the examples + above). When your object is created, it will automatically assign the value + you want to bind `to` based on the name of your binding key. In the + examples above, during init, Ember objects will effectively call + something like this on your binding: + + ```javascript + binding = Ember.Binding.from(this.valueBinding).to("value"); + ``` + + This creates a new binding instance based on the template you provide, and + sets the to path to the `value` property of the new object. Now that the + binding is fully configured with a `from` and a `to`, it simply needs to be + connected to become active. This is done through the `connect()` method: + + ```javascript + binding.connect(this); + ``` + + Note that when you connect a binding you pass the object you want it to be + connected to. This object will be used as the root for both the from and + to side of the binding when inspecting relative paths. This allows the + binding to be automatically inherited by subclassed objects as well. + + Now that the binding is connected, it will observe both the from and to side + and relay changes. + + If you ever needed to do so (you almost never will, but it is useful to + understand this anyway), you could manually create an active binding by + using the `Ember.bind()` helper method. (This is the same method used by + to setup your bindings on objects): + + ```javascript + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + ``` + + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: + + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", + + // OTHER CODE FOR THIS OBJECT... + }); + ``` + + Ember's built in binding creation method makes it easy to automatically + create bindings for you. You should always use the highest-level APIs + available, even if you understand how it works underneath. + + @class Binding + @namespace Ember + @since Ember 0.9 +*/ +Ember.Binding = Binding; + + +/** + Global helper method to create a new binding. Just pass the root object + along with a `to` and `from` path to create and connect the binding. + + @method bind + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance +*/ +Ember.bind = function(obj, to, from) { + return new Ember.Binding(to, from).connect(obj); +}; + +/** + @method oneWay + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance +*/ +Ember.oneWay = function(obj, to, from) { + return new Ember.Binding(to, from).oneWay().connect(obj); +}; + +})(); + + + +(function() { +/** +@module ember-metal +*/ + +var Mixin, REQUIRED, Alias, + a_map = Ember.ArrayPolyfills.map, + a_indexOf = Ember.ArrayPolyfills.indexOf, + a_forEach = Ember.ArrayPolyfills.forEach, + a_slice = [].slice, + o_create = Ember.create, + defineProperty = Ember.defineProperty, + guidFor = Ember.guidFor; + +function mixinsMeta(obj) { + var m = Ember.meta(obj, true), ret = m.mixins; + if (!ret) { + ret = m.mixins = {}; + } else if (!m.hasOwnProperty('mixins')) { + ret = m.mixins = o_create(ret); + } + return ret; +} + +function initMixin(mixin, args) { + if (args && args.length > 0) { + mixin.mixins = a_map.call(args, function(x) { + if (x instanceof Mixin) { return x; } + + // Note: Manually setup a primitive mixin here. This is the only + // way to actually get a primitive mixin. This way normal creation + // of mixins will give you combined mixins... + var mixin = new Mixin(); + mixin.properties = x; + return mixin; + }); + } + return mixin; +} + +function isMethod(obj) { + return 'function' === typeof obj && + obj.isMethod !== false && + obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; +} + +var CONTINUE = {}; + +function mixinProperties(mixinsMeta, mixin) { + var guid; + + if (mixin instanceof Mixin) { + guid = guidFor(mixin); + if (mixinsMeta[guid]) { return CONTINUE; } + mixinsMeta[guid] = mixin; + return mixin.properties; + } else { + return mixin; // apply anonymous mixin properties + } +} + +function concatenatedProperties(props, values, base) { + var concats; + + // reset before adding each new mixin to pickup concats from previous + concats = values.concatenatedProperties || base.concatenatedProperties; + if (props.concatenatedProperties) { + concats = concats ? concats.concat(props.concatenatedProperties) : props.concatenatedProperties; + } + + return concats; +} + +function giveDescriptorSuper(meta, key, property, values, descs) { + var superProperty; + + // Computed properties override methods, and do not call super to them + if (values[key] === undefined) { + // Find the original descriptor in a parent mixin + superProperty = descs[key]; + } + + // If we didn't find the original descriptor in a parent mixin, find + // it on the original object. + superProperty = superProperty || meta.descs[key]; + + if (!superProperty || !(superProperty instanceof Ember.ComputedProperty)) { + return property; + } + + // Since multiple mixins may inherit from the same parent, we need + // to clone the computed property so that other mixins do not receive + // the wrapped version. + property = o_create(property); + property.func = Ember.wrap(property.func, superProperty.func); + + return property; +} + +function giveMethodSuper(obj, key, method, values, descs) { + var superMethod; + + // Methods overwrite computed properties, and do not call super to them. + if (descs[key] === undefined) { + // Find the original method in a parent mixin + superMethod = values[key]; + } + + // If we didn't find the original value in a parent mixin, find it in + // the original object + superMethod = superMethod || obj[key]; + + // Only wrap the new method if the original method was a function + if ('function' !== typeof superMethod) { + return method; + } + + return Ember.wrap(method, superMethod); +} + +function applyConcatenatedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; + + if (baseValue) { + if ('function' === typeof baseValue.concat) { + return baseValue.concat(value); + } else { + return Ember.makeArray(baseValue).concat(value); + } + } else { + return Ember.makeArray(value); + } +} + +function addNormalizedProperty(base, key, value, meta, descs, values, concats) { + if (value instanceof Ember.Descriptor) { + if (value === REQUIRED && descs[key]) { return CONTINUE; } + + // Wrap descriptor function to implement + // _super() if needed + if (value.func) { + value = giveDescriptorSuper(meta, key, value, values, descs); + } + + descs[key] = value; + values[key] = undefined; + } else { + // impl super if needed... + if (isMethod(value)) { + value = giveMethodSuper(base, key, value, values, descs); + } else if ((concats && a_indexOf.call(concats, key) >= 0) || key === 'concatenatedProperties') { + value = applyConcatenatedProperties(base, key, value, values); + } + + descs[key] = undefined; + values[key] = value; + } +} + +function mergeMixins(mixins, m, descs, values, base, keys) { + var mixin, props, key, concats, meta; + + function removeKeys(keyName) { + delete descs[keyName]; + delete values[keyName]; + } + + for(var i=0, l=mixins.length; i<l; i++) { + mixin = mixins[i]; + Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); + + props = mixinProperties(m, mixin); + if (props === CONTINUE) { continue; } + + if (props) { + meta = Ember.meta(base); + concats = concatenatedProperties(props, values, base); + + for (key in props) { + if (!props.hasOwnProperty(key)) { continue; } + keys.push(key); + addNormalizedProperty(base, key, props[key], meta, descs, values, concats); + } + + // manually copy toString() because some JS engines do not enumerate it + if (props.hasOwnProperty('toString')) { base.toString = props.toString; } + } else if (mixin.mixins) { + mergeMixins(mixin.mixins, m, descs, values, base, keys); + if (mixin._without) { a_forEach.call(mixin._without, removeKeys); } + } + } +} + +var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/; + +function detectBinding(obj, key, value, m) { + if (IS_BINDING.test(key)) { + var bindings = m.bindings; + if (!bindings) { + bindings = m.bindings = {}; + } else if (!m.hasOwnProperty('bindings')) { + bindings = m.bindings = o_create(m.bindings); + } + bindings[key] = value; + } +} + +function connectBindings(obj, m) { + // TODO Mixin.apply(instance) should disconnect binding if exists + var bindings = m.bindings, key, binding, to; + if (bindings) { + for (key in bindings) { + binding = bindings[key]; + if (binding) { + to = key.slice(0, -7); // strip Binding off end + if (binding instanceof Ember.Binding) { + binding = binding.copy(); // copy prototypes' instance + binding.to(to); + } else { // binding is string path + binding = new Ember.Binding(to, binding); + } + binding.connect(obj); + obj[key] = binding; + } + } + // mark as applied + m.bindings = {}; + } +} + +function finishPartial(obj, m) { + connectBindings(obj, m || Ember.meta(obj)); + return obj; +} + +function followAlias(obj, desc, m, descs, values) { + var altKey = desc.methodName, value; + if (descs[altKey] || values[altKey]) { + value = values[altKey]; + desc = descs[altKey]; + } else if (m.descs[altKey]) { + desc = m.descs[altKey]; + value = undefined; + } else { + desc = undefined; + value = obj[altKey]; + } + + return { desc: desc, value: value }; +} + +function updateObservers(obj, key, observer, observerKey, method) { + if ('function' !== typeof observer) { return; } + + var paths = observer[observerKey]; + + if (paths) { + for (var i=0, l=paths.length; i<l; i++) { + Ember[method](obj, paths[i], null, key); + } + } +} + +function replaceObservers(obj, key, observer) { + var prevObserver = obj[key]; + + updateObservers(obj, key, prevObserver, '__ember_observesBefore__', 'removeBeforeObserver'); + updateObservers(obj, key, prevObserver, '__ember_observes__', 'removeObserver'); + + updateObservers(obj, key, observer, '__ember_observesBefore__', 'addBeforeObserver'); + updateObservers(obj, key, observer, '__ember_observes__', 'addObserver'); +} + +function applyMixin(obj, mixins, partial) { + var descs = {}, values = {}, m = Ember.meta(obj), + key, value, desc, keys = []; + + // Go through all mixins and hashes passed in, and: + // + // * Handle concatenated properties + // * Set up _super wrapping if necessary + // * Set up computed property descriptors + // * Copying `toString` in broken browsers + mergeMixins(mixins, mixinsMeta(obj), descs, values, obj, keys); + + for(var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + if (key === 'constructor' || !values.hasOwnProperty(key)) { continue; } + + desc = descs[key]; + value = values[key]; + + if (desc === REQUIRED) { continue; } + + while (desc && desc instanceof Alias) { + var followed = followAlias(obj, desc, m, descs, values); + desc = followed.desc; + value = followed.value; + } + + if (desc === undefined && value === undefined) { continue; } + + replaceObservers(obj, key, value); + detectBinding(obj, key, value, m); + defineProperty(obj, key, desc, value, m); + } + + if (!partial) { // don't apply to prototype + finishPartial(obj, m); + } + + return obj; +} + +/** + @method mixin + @for Ember + @param obj + @param mixins* + @return obj +*/ +Ember.mixin = function(obj) { + var args = a_slice.call(arguments, 1); + applyMixin(obj, args, false); + return obj; +}; + +/** + The `Ember.Mixin` class allows you to create mixins, whose properties can be + added to other classes. For instance, + + ```javascript + App.Editable = Ember.Mixin.create({ + edit: function() { + console.log('starting to edit'); + this.set('isEditing', true); + }, + isEditing: false + }); + + // Mix mixins into classes by passing them as the first arguments to + // .extend. + App.CommentView = Ember.View.extend(App.Editable, { + template: Ember.Handlebars.compile('{{#if isEditing}}...{{else}}...{{/if}}') + }); + + commentView = App.CommentView.create(); + commentView.edit(); // outputs 'starting to edit' + ``` + + Note that Mixins are created with `Ember.Mixin.create`, not + `Ember.Mixin.extend`. + + @class Mixin + @namespace Ember +*/ +Ember.Mixin = function() { return initMixin(this, arguments); }; + +Mixin = Ember.Mixin; + +Mixin.prototype = { + properties: null, + mixins: null, + ownerConstructor: null +}; + +Mixin._apply = applyMixin; + +Mixin.applyPartial = function(obj) { + var args = a_slice.call(arguments, 1); + return applyMixin(obj, args, true); +}; + +Mixin.finishPartial = finishPartial; + +Ember.anyUnprocessedMixins = false; + +/** + Creates an instance of a class. Accepts either no arguments, or an object + containing values to initialize the newly instantiated object with. + + ```javascript + App.Person = Ember.Object.extend({ + helloWorld: function() { + alert("Hi, my name is " + this.get('name')); + } + }); + + var tom = App.Person.create({ + name: 'Tom Dale' + }); + + tom.helloWorld(); // alerts "Hi, my name is Tom Dale". + ``` + + `create` will call the `init` function if defined during + `Ember.AnyObject.extend` + + If no arguments are passed to `create`, it will not set values to the new + instance during initialization: + + ```javascript + var noName = App.Person.create(); + noName.helloWorld(); // alerts undefined + ``` + + NOTE: For performance reasons, you cannot declare methods or computed + properties during `create`. You should instead declare methods and computed + properties when using `extend`. + + @method create + @static + @param arguments* +*/ +Mixin.create = function() { + Ember.anyUnprocessedMixins = true; + var M = this; + return initMixin(new M(), arguments); +}; + +var MixinPrototype = Mixin.prototype; + +/** + @method reopen + @param arguments* +*/ +MixinPrototype.reopen = function() { + var mixin, tmp; + + if (this.properties) { + mixin = Mixin.create(); + mixin.properties = this.properties; + delete this.properties; + this.mixins = [mixin]; + } else if (!this.mixins) { + this.mixins = []; + } + + var len = arguments.length, mixins = this.mixins, idx; + + for(idx=0; idx < len; idx++) { + mixin = arguments[idx]; + Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); + + if (mixin instanceof Mixin) { + mixins.push(mixin); + } else { + tmp = Mixin.create(); + tmp.properties = mixin; + mixins.push(tmp); + } + } + + return this; +}; + +/** + @method apply + @param obj + @return applied object +*/ +MixinPrototype.apply = function(obj) { + return applyMixin(obj, [this], false); +}; + +MixinPrototype.applyPartial = function(obj) { + return applyMixin(obj, [this], true); +}; + +function _detect(curMixin, targetMixin, seen) { + var guid = guidFor(curMixin); + + if (seen[guid]) { return false; } + seen[guid] = true; + + if (curMixin === targetMixin) { return true; } + var mixins = curMixin.mixins, loc = mixins ? mixins.length : 0; + while (--loc >= 0) { + if (_detect(mixins[loc], targetMixin, seen)) { return true; } + } + return false; +} + +/** + @method detect + @param obj + @return {Boolean} +*/ +MixinPrototype.detect = function(obj) { + if (!obj) { return false; } + if (obj instanceof Mixin) { return _detect(obj, this, {}); } + var mixins = Ember.meta(obj, false).mixins; + if (mixins) { + return !!mixins[guidFor(this)]; + } + return false; +}; + +MixinPrototype.without = function() { + var ret = new Mixin(this); + ret._without = a_slice.call(arguments); + return ret; +}; + +function _keys(ret, mixin, seen) { + if (seen[guidFor(mixin)]) { return; } + seen[guidFor(mixin)] = true; + + if (mixin.properties) { + var props = mixin.properties; + for (var key in props) { + if (props.hasOwnProperty(key)) { ret[key] = true; } + } + } else if (mixin.mixins) { + a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); }); + } +} + +MixinPrototype.keys = function() { + var keys = {}, seen = {}, ret = []; + _keys(keys, this, seen); + for(var key in keys) { + if (keys.hasOwnProperty(key)) { ret.push(key); } + } + return ret; +}; + +// returns the mixins currently applied to the specified object +// TODO: Make Ember.mixin +Mixin.mixins = function(obj) { + var mixins = Ember.meta(obj, false).mixins, ret = []; + + if (!mixins) { return ret; } + + for (var key in mixins) { + var mixin = mixins[key]; + + // skip primitive mixins since these are always anonymous + if (!mixin.properties) { ret.push(mixin); } + } + + return ret; +}; + +REQUIRED = new Ember.Descriptor(); +REQUIRED.toString = function() { return '(Required Property)'; }; + +/** + Denotes a required property for a mixin + + @method required + @for Ember +*/ +Ember.required = function() { + return REQUIRED; +}; + +Alias = function(methodName) { + this.methodName = methodName; +}; +Alias.prototype = new Ember.Descriptor(); + +/** + Makes a property or method available via an additional name. + + ```javascript + App.PaintSample = Ember.Object.extend({ + color: 'red', + colour: Ember.alias('color'), + name: function(){ + return "Zed"; + }, + moniker: Ember.alias("name") + }); + + var paintSample = App.PaintSample.create() + paintSample.get('colour'); // 'red' + paintSample.moniker(); // 'Zed' + ``` + + @method alias + @for Ember + @param {String} methodName name of the method or property to alias + @return {Ember.Descriptor} + @deprecated Use `Ember.aliasMethod` or `Ember.computed.alias` instead +*/ +Ember.alias = function(methodName) { + return new Alias(methodName); +}; + +Ember.alias = Ember.deprecateFunc("Ember.alias is deprecated. Please use Ember.aliasMethod or Ember.computed.alias instead.", Ember.alias); + +/** + Makes a method available via an additional name. + + ```javascript + App.Person = Ember.Object.extend({ + name: function(){ + return 'Tomhuda Katzdale'; + }, + moniker: Ember.aliasMethod('name') + }); + + var goodGuy = App.Person.create() + ``` + + @method aliasMethod + @for Ember + @param {String} methodName name of the method to alias + @return {Ember.Descriptor} +*/ +Ember.aliasMethod = function(methodName) { + return new Alias(methodName); +}; + +// .......................................................... +// OBSERVER HELPER +// + +/** + @method observer + @for Ember + @param {Function} func + @param {String} propertyNames* + @return func +*/ +Ember.observer = function(func) { + var paths = a_slice.call(arguments, 1); + func.__ember_observes__ = paths; + return func; +}; + +// If observers ever become asynchronous, Ember.immediateObserver +// must remain synchronous. +/** + @method immediateObserver + @for Ember + @param {Function} func + @param {String} propertyNames* + @return func +*/ +Ember.immediateObserver = function() { + for (var i=0, l=arguments.length; i<l; i++) { + var arg = arguments[i]; + Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.", typeof arg !== "string" || arg.indexOf('.') === -1); + } + + return Ember.observer.apply(this, arguments); +}; + +/** + When observers fire, they are called with the arguments `obj`, `keyName` + and `value`. In a typical observer, value is the new, post-change value. + + A `beforeObserver` fires before a property changes. The `value` argument contains + the pre-change value. + + A `beforeObserver` is an alternative form of `.observesBefore()`. + + ```javascript + App.PersonView = Ember.View.extend({ + valueWillChange: function (obj, keyName, value) { + this.changingFrom = value; + }.observesBefore('content.value'), + valueDidChange: function(obj, keyName, value) { + // only run if updating a value already in the DOM + if(this.get('state') === 'inDOM') { + var color = value > this.changingFrom ? 'green' : 'red'; + // logic + } + }.observes('content.value') + }); + ``` + + @method beforeObserver + @for Ember + @param {Function} func + @param {String} propertyNames* + @return func +*/ +Ember.beforeObserver = function(func) { + var paths = a_slice.call(arguments, 1); + func.__ember_observesBefore__ = paths; + return func; +}; + +})(); + + + +(function() { +/** +Ember Metal + +@module ember +@submodule ember-metal +*/ + +})(); + +(function() { +define("rsvp/all", + ["rsvp/defer","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var defer = __dependency1__.defer; + + function all(promises) { + var results = [], deferred = defer(), remaining = promises.length; + + if (remaining === 0) { + deferred.resolve([]); + } + + var resolver = function(index) { + return function(value) { + resolveAll(index, value); + }; + }; + + var resolveAll = function(index, value) { + results[index] = value; + if (--remaining === 0) { + deferred.resolve(results); + } + }; + + var rejectAll = function(error) { + deferred.reject(error); + }; + + for (var i = 0; i < promises.length; i++) { + if (promises[i] && typeof promises[i].then === 'function') { + promises[i].then(resolver(i), rejectAll); + } else { + resolveAll(i, promises[i]); + } + } + return deferred.promise; + } + + __exports__.all = all; + }); + +define("rsvp/async", + ["exports"], + function(__exports__) { + "use strict"; + var browserGlobal = (typeof window !== 'undefined') ? window : {}; + + var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; + var async; + + if (typeof process !== 'undefined' && + {}.toString.call(process) === '[object process]') { + async = function(callback, binding) { + process.nextTick(function() { + callback.call(binding); + }); + }; + } else if (BrowserMutationObserver) { + var queue = []; + + var observer = new BrowserMutationObserver(function() { + var toProcess = queue.slice(); + queue = []; + + toProcess.forEach(function(tuple) { + var callback = tuple[0], binding = tuple[1]; + callback.call(binding); + }); + }); + + var element = document.createElement('div'); + observer.observe(element, { attributes: true }); + + // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661 + window.addEventListener('unload', function(){ + observer.disconnect(); + observer = null; + }); + + async = function(callback, binding) { + queue.push([callback, binding]); + element.setAttribute('drainQueue', 'drainQueue'); + }; + } else { + async = function(callback, binding) { + setTimeout(function() { + callback.call(binding); + }, 1); + }; + } + + + __exports__.async = async; + }); + +define("rsvp/config", + ["rsvp/async","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var async = __dependency1__.async; + + var config = {}; + config.async = async; + + __exports__.config = config; + }); + +define("rsvp/defer", + ["rsvp/promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__.Promise; + + function defer() { + var deferred = {}; + + var promise = new Promise(function(resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + + deferred.promise = promise; + return deferred; + } + + __exports__.defer = defer; + }); + +define("rsvp/events", + ["exports"], + function(__exports__) { + "use strict"; + var Event = function(type, options) { + this.type = type; + + for (var option in options) { + if (!options.hasOwnProperty(option)) { continue; } + + this[option] = options[option]; + } + }; + + var indexOf = function(callbacks, callback) { + for (var i=0, l=callbacks.length; i<l; i++) { + if (callbacks[i][0] === callback) { return i; } + } + + return -1; + }; + + var callbacksFor = function(object) { + var callbacks = object._promiseCallbacks; + + if (!callbacks) { + callbacks = object._promiseCallbacks = {}; + } + + return callbacks; + }; + + var EventTarget = { + mixin: function(object) { + object.on = this.on; + object.off = this.off; + object.trigger = this.trigger; + return object; + }, + + on: function(eventNames, callback, binding) { + var allCallbacks = callbacksFor(this), callbacks, eventName; + eventNames = eventNames.split(/\s+/); + binding = binding || this; + + while (eventName = eventNames.shift()) { + callbacks = allCallbacks[eventName]; + + if (!callbacks) { + callbacks = allCallbacks[eventName] = []; + } + + if (indexOf(callbacks, callback) === -1) { + callbacks.push([callback, binding]); + } + } + }, + + off: function(eventNames, callback) { + var allCallbacks = callbacksFor(this), callbacks, eventName, index; + eventNames = eventNames.split(/\s+/); + + while (eventName = eventNames.shift()) { + if (!callback) { + allCallbacks[eventName] = []; + continue; + } + + callbacks = allCallbacks[eventName]; + + index = indexOf(callbacks, callback); + + if (index !== -1) { callbacks.splice(index, 1); } + } + }, + + trigger: function(eventName, options) { + var allCallbacks = callbacksFor(this), + callbacks, callbackTuple, callback, binding, event; + + if (callbacks = allCallbacks[eventName]) { + // Don't cache the callbacks.length since it may grow + for (var i=0; i<callbacks.length; i++) { + callbackTuple = callbacks[i]; + callback = callbackTuple[0]; + binding = callbackTuple[1]; + + if (typeof options !== 'object') { + options = { detail: options }; + } + + event = new Event(eventName, options); + callback.call(binding, event); + } + } + } + }; + + + __exports__.EventTarget = EventTarget; + }); + +define("rsvp/hash", + ["rsvp/defer","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var defer = __dependency1__.defer; + + function size(object) { + var size = 0; + + for (var prop in object) { + size++; + } + + return size; + } + + function hash(promises) { + var results = {}, deferred = defer(), remaining = size(promises); + + if (remaining === 0) { + deferred.resolve({}); + } + + var resolver = function(prop) { + return function(value) { + resolveAll(prop, value); + }; + }; + + var resolveAll = function(prop, value) { + results[prop] = value; + if (--remaining === 0) { + deferred.resolve(results); + } + }; + + var rejectAll = function(error) { + deferred.reject(error); + }; + + for (var prop in promises) { + if (promises[prop] && typeof promises[prop].then === 'function') { + promises[prop].then(resolver(prop), rejectAll); + } else { + resolveAll(prop, promises[prop]); + } + } + + return deferred.promise; + } + + __exports__.hash = hash; + }); + +define("rsvp/node", + ["rsvp/promise","rsvp/all","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Promise = __dependency1__.Promise; + var all = __dependency2__.all; + + function makeNodeCallbackFor(resolve, reject) { + return function (error, value) { + if (error) { + reject(error); + } else if (arguments.length > 2) { + resolve(Array.prototype.slice.call(arguments, 1)); + } else { + resolve(value); + } + }; + } + + function denodeify(nodeFunc) { + return function() { + var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject; + + var promise = new Promise(function(nodeResolve, nodeReject) { + resolve = nodeResolve; + reject = nodeReject; + }); + + all(nodeArgs).then(function(nodeArgs) { + nodeArgs.push(makeNodeCallbackFor(resolve, reject)); + + try { + nodeFunc.apply(this, nodeArgs); + } catch(e) { + reject(e); + } + }); + + return promise; + }; + } + + __exports__.denodeify = denodeify; + }); + +define("rsvp/promise", + ["rsvp/config","rsvp/events","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var config = __dependency1__.config; + var EventTarget = __dependency2__.EventTarget; + + function objectOrFunction(x) { + return isFunction(x) || (typeof x === "object" && x !== null); + } + + function isFunction(x){ + return typeof x === "function"; + } + + var Promise = function(resolver) { + var promise = this, + resolved = false; + + if (typeof resolver !== 'function') { + throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor'); + } + + if (!(promise instanceof Promise)) { + return new Promise(resolver); + } + + var resolvePromise = function(value) { + if (resolved) { return; } + resolved = true; + resolve(promise, value); + }; + + var rejectPromise = function(value) { + if (resolved) { return; } + resolved = true; + reject(promise, value); + }; + + this.on('promise:resolved', function(event) { + this.trigger('success', { detail: event.detail }); + }, this); + + this.on('promise:failed', function(event) { + this.trigger('error', { detail: event.detail }); + }, this); + + try { + resolver(resolvePromise, rejectPromise); + } catch(e) { + rejectPromise(e); + } + }; + + var invokeCallback = function(type, promise, callback, event) { + var hasCallback = isFunction(callback), + value, error, succeeded, failed; + + if (hasCallback) { + try { + value = callback(event.detail); + succeeded = true; + } catch(e) { + failed = true; + error = e; + } + } else { + value = event.detail; + succeeded = true; + } + + if (handleThenable(promise, value)) { + return; + } else if (hasCallback && succeeded) { + resolve(promise, value); + } else if (failed) { + reject(promise, error); + } else if (type === 'resolve') { + resolve(promise, value); + } else if (type === 'reject') { + reject(promise, value); + } + }; + + Promise.prototype = { + constructor: Promise, + + then: function(done, fail) { + var thenPromise = new Promise(function() {}); + + if (this.isFulfilled) { + config.async(function() { + invokeCallback('resolve', thenPromise, done, { detail: this.fulfillmentValue }); + }, this); + } + + if (this.isRejected) { + config.async(function() { + invokeCallback('reject', thenPromise, fail, { detail: this.rejectedReason }); + }, this); + } + + this.on('promise:resolved', function(event) { + invokeCallback('resolve', thenPromise, done, event); + }); + + this.on('promise:failed', function(event) { + invokeCallback('reject', thenPromise, fail, event); + }); + + return thenPromise; + } + }; + + EventTarget.mixin(Promise.prototype); + + function resolve(promise, value) { + if (promise === value) { + fulfill(promise, value); + } else if (!handleThenable(promise, value)) { + fulfill(promise, value); + } + } + + function handleThenable(promise, value) { + var then = null; + + if (objectOrFunction(value)) { + try { + then = value.then; + } catch(e) { + reject(promise, e); + return true; + } + + if (isFunction(then)) { + try { + then.call(value, function(val) { + if (value !== val) { + resolve(promise, val); + } else { + fulfill(promise, val); + } + }, function(val) { + reject(promise, val); + }); + } catch (e) { + reject(promise, e); + } + return true; + } + } + + return false; + } + + function fulfill(promise, value) { + config.async(function() { + promise.trigger('promise:resolved', { detail: value }); + promise.isFulfilled = true; + promise.fulfillmentValue = value; + }); + } + + function reject(promise, value) { + config.async(function() { + promise.trigger('promise:failed', { detail: value }); + promise.isRejected = true; + promise.rejectedReason = value; + }); + } + + + __exports__.Promise = Promise; + }); + +define("rsvp/reject", + ["rsvp/promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__.Promise; + + + function objectOrFunction(x) { + return typeof x === "function" || (typeof x === "object" && x !== null); + } + + + function reject(reason) { + return new Promise(function (resolve, reject) { + reject(reason); + }); + } + + + __exports__.reject = reject; + }); + +define("rsvp/resolve", + ["rsvp/promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__.Promise; + + + function objectOrFunction(x) { + return typeof x === "function" || (typeof x === "object" && x !== null); + } + + function resolve(thenable){ + var promise = new Promise(function(resolve, reject){ + var then; + + try { + if ( objectOrFunction(thenable) ) { + then = thenable.then; + + if (typeof then === "function") { + then.call(thenable, resolve, reject); + } else { + resolve(thenable); + } + + } else { + resolve(thenable); + } + + } catch(error) { + reject(error); + } + }); + + return promise; + } + + + __exports__.resolve = resolve; + }); + +define("rsvp", + ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { + "use strict"; + var EventTarget = __dependency1__.EventTarget; + var Promise = __dependency2__.Promise; + var denodeify = __dependency3__.denodeify; + var all = __dependency4__.all; + var hash = __dependency5__.hash; + var defer = __dependency6__.defer; + var config = __dependency7__.config; + var resolve = __dependency8__.resolve; + var reject = __dependency9__.reject; + + function configure(name, value) { + config[name] = value; + } + + + __exports__.Promise = Promise; + __exports__.EventTarget = EventTarget; + __exports__.all = all; + __exports__.hash = hash; + __exports__.defer = defer; + __exports__.denodeify = denodeify; + __exports__.configure = configure; + __exports__.resolve = resolve; + __exports__.reject = reject; + }); + + + +})(); + +(function() { +define("container", + [], + function() { + + function InheritingDict(parent) { + this.parent = parent; + this.dict = {}; + } + + InheritingDict.prototype = { + get: function(key) { + var dict = this.dict; + + if (dict.hasOwnProperty(key)) { + return dict[key]; + } + + if (this.parent) { + return this.parent.get(key); + } + }, + + set: function(key, value) { + this.dict[key] = value; + }, + + has: function(key) { + var dict = this.dict; + + if (dict.hasOwnProperty(key)) { + return true; + } + + if (this.parent) { + return this.parent.has(key); + } + + return false; + }, + + eachLocal: function(callback, binding) { + var dict = this.dict; + + for (var prop in dict) { + if (dict.hasOwnProperty(prop)) { + callback.call(binding, prop, dict[prop]); + } + } + } + }; + + function Container(parent) { + this.parent = parent; + this.children = []; + + this.resolver = parent && parent.resolver || function() {}; + this.registry = new InheritingDict(parent && parent.registry); + this.cache = new InheritingDict(parent && parent.cache); + this.typeInjections = new InheritingDict(parent && parent.typeInjections); + this.injections = {}; + this._options = new InheritingDict(parent && parent._options); + this._typeOptions = new InheritingDict(parent && parent._typeOptions); + } + + Container.prototype = { + child: function() { + var container = new Container(this); + this.children.push(container); + return container; + }, + + set: function(object, key, value) { + object[key] = value; + }, + + register: function(type, name, factory, options) { + var fullName; + + if (type.indexOf(':') !== -1){ + options = factory; + factory = name; + fullName = type; + } else { + Ember.deprecate('register("'+type +'", "'+ name+'") is now deprecated in-favour of register("'+type+':'+name+'");', false); + fullName = type + ":" + name; + } + + var normalizedName = this.normalize(fullName); + + this.registry.set(normalizedName, factory); + this._options.set(normalizedName, options || {}); + }, + + resolve: function(fullName) { + return this.resolver(fullName) || this.registry.get(fullName); + }, + + normalize: function(fullName) { + return fullName; + }, + + lookup: function(fullName, options) { + fullName = this.normalize(fullName); + + options = options || {}; + + if (this.cache.has(fullName) && options.singleton !== false) { + return this.cache.get(fullName); + } + + var value = instantiate(this, fullName); + + if (!value) { return; } + + if (isSingleton(this, fullName) && options.singleton !== false) { + this.cache.set(fullName, value); + } + + return value; + }, + + lookupFactory: function(fullName) { + return factoryFor(this, fullName); + }, + + has: function(fullName) { + if (this.cache.has(fullName)) { + return true; + } + + return !!factoryFor(this, fullName); + }, + + optionsForType: function(type, options) { + if (this.parent) { illegalChildOperation('optionsForType'); } + + this._typeOptions.set(type, options); + }, + + options: function(type, options) { + this.optionsForType(type, options); + }, + + typeInjection: function(type, property, fullName) { + if (this.parent) { illegalChildOperation('typeInjection'); } + + var injections = this.typeInjections.get(type); + if (!injections) { + injections = []; + this.typeInjections.set(type, injections); + } + injections.push({ property: property, fullName: fullName }); + }, + + injection: function(factoryName, property, injectionName) { + if (this.parent) { illegalChildOperation('injection'); } + + if (factoryName.indexOf(':') === -1) { + return this.typeInjection(factoryName, property, injectionName); + } + + var injections = this.injections[factoryName] = this.injections[factoryName] || []; + injections.push({ property: property, fullName: injectionName }); + }, + + destroy: function() { + this.isDestroyed = true; + + for (var i=0, l=this.children.length; i<l; i++) { + this.children[i].destroy(); + } + + this.children = []; + + eachDestroyable(this, function(item) { + item.destroy(); + }); + + delete this.parent; + this.isDestroyed = true; + }, + + reset: function() { + for (var i=0, l=this.children.length; i<l; i++) { + resetCache(this.children[i]); + } + resetCache(this); + } + }; + + function illegalChildOperation(operation) { + throw new Error(operation + " is not currently supported on child containers"); + } + + function isSingleton(container, fullName) { + var singleton = option(container, fullName, 'singleton'); + + return singleton !== false; + } + + function buildInjections(container, injections) { + var hash = {}; + + if (!injections) { return hash; } + + var injection, lookup; + + for (var i=0, l=injections.length; i<l; i++) { + injection = injections[i]; + lookup = container.lookup(injection.fullName); + hash[injection.property] = lookup; + } + + return hash; + } + + function option(container, fullName, optionName) { + var options = container._options.get(fullName); + + if (options && options[optionName] !== undefined) { + return options[optionName]; + } + + var type = fullName.split(":")[0]; + options = container._typeOptions.get(type); + + if (options) { + return options[optionName]; + } + } + + function factoryFor(container, fullName) { + var name = container.normalize(fullName); + return container.resolve(name); + } + + function instantiate(container, fullName) { + var factory = factoryFor(container, fullName); + + var splitName = fullName.split(":"), + type = splitName[0], + value; + + if (option(container, fullName, 'instantiate') === false) { + return factory; + } + + if (factory) { + var injections = []; + injections = injections.concat(container.typeInjections.get(type) || []); + injections = injections.concat(container.injections[fullName] || []); + + var hash = buildInjections(container, injections); + hash.container = container; + hash._debugContainerKey = fullName; + + value = factory.create(hash); + + return value; + } + } + + function eachDestroyable(container, callback) { + container.cache.eachLocal(function(key, value) { + if (option(container, key, 'instantiate') === false) { return; } + callback(value); + }); + } + + function resetCache(container) { + container.cache.eachLocal(function(key, value) { + if (option(container, key, 'instantiate') === false) { return; } + value.destroy(); + }); + container.cache.dict = {}; + } + + return Container; +}); + +})(); + +(function() { +/*globals ENV */ +/** +@module ember +@submodule ember-runtime +*/ + +var indexOf = Ember.EnumerableUtils.indexOf; + +/** + This will compare two javascript values of possibly different types. + It will tell you which one is greater than the other by returning: + + - -1 if the first is smaller than the second, + - 0 if both are equal, + - 1 if the first is greater than the second. + + The order is calculated based on `Ember.ORDER_DEFINITION`, if types are different. + In case they have the same type an appropriate comparison for this type is made. + + ```javascript + Ember.compare('hello', 'hello'); // 0 + Ember.compare('abc', 'dfg'); // -1 + Ember.compare(2, 1); // 1 + ``` + + @method compare + @for Ember + @param {Object} v First value to compare + @param {Object} w Second value to compare + @return {Number} -1 if v < w, 0 if v = w and 1 if v > w. +*/ +Ember.compare = function compare(v, w) { + if (v === w) { return 0; } + + var type1 = Ember.typeOf(v); + var type2 = Ember.typeOf(w); + + var Comparable = Ember.Comparable; + if (Comparable) { + if (type1==='instance' && Comparable.detect(v.constructor)) { + return v.constructor.compare(v, w); + } + + if (type2 === 'instance' && Comparable.detect(w.constructor)) { + return 1-w.constructor.compare(w, v); + } + } + + // If we haven't yet generated a reverse-mapping of Ember.ORDER_DEFINITION, + // do so now. + var mapping = Ember.ORDER_DEFINITION_MAPPING; + if (!mapping) { + var order = Ember.ORDER_DEFINITION; + mapping = Ember.ORDER_DEFINITION_MAPPING = {}; + var idx, len; + for (idx = 0, len = order.length; idx < len; ++idx) { + mapping[order[idx]] = idx; + } + + // We no longer need Ember.ORDER_DEFINITION. + delete Ember.ORDER_DEFINITION; + } + + var type1Index = mapping[type1]; + var type2Index = mapping[type2]; + + if (type1Index < type2Index) { return -1; } + if (type1Index > type2Index) { return 1; } + + // types are equal - so we have to check values now + switch (type1) { + case 'boolean': + case 'number': + if (v < w) { return -1; } + if (v > w) { return 1; } + return 0; + + case 'string': + var comp = v.localeCompare(w); + if (comp < 0) { return -1; } + if (comp > 0) { return 1; } + return 0; + + case 'array': + var vLen = v.length; + var wLen = w.length; + var l = Math.min(vLen, wLen); + var r = 0; + var i = 0; + while (r === 0 && i < l) { + r = compare(v[i],w[i]); + i++; + } + if (r !== 0) { return r; } + + // all elements are equal now + // shorter array should be ordered first + if (vLen < wLen) { return -1; } + if (vLen > wLen) { return 1; } + // arrays are equal now + return 0; + + case 'instance': + if (Ember.Comparable && Ember.Comparable.detect(v)) { + return v.compare(v, w); + } + return 0; + + case 'date': + var vNum = v.getTime(); + var wNum = w.getTime(); + if (vNum < wNum) { return -1; } + if (vNum > wNum) { return 1; } + return 0; + + default: + return 0; + } +}; + +function _copy(obj, deep, seen, copies) { + var ret, loc, key; + + // primitive data types are immutable, just return them. + if ('object' !== typeof obj || obj===null) return obj; + + // avoid cyclical loops + if (deep && (loc=indexOf(seen, obj))>=0) return copies[loc]; + + Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', !(obj instanceof Ember.Object) || (Ember.Copyable && Ember.Copyable.detect(obj))); + + // IMPORTANT: this specific test will detect a native array only. Any other + // object will need to implement Copyable. + if (Ember.typeOf(obj) === 'array') { + ret = obj.slice(); + if (deep) { + loc = ret.length; + while(--loc>=0) ret[loc] = _copy(ret[loc], deep, seen, copies); + } + } else if (Ember.Copyable && Ember.Copyable.detect(obj)) { + ret = obj.copy(deep, seen, copies); + } else { + ret = {}; + for(key in obj) { + if (!obj.hasOwnProperty(key)) continue; + + // Prevents browsers that don't respect non-enumerability from + // copying internal Ember properties + if (key.substring(0,2) === '__') continue; + + ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; + } + } + + if (deep) { + seen.push(obj); + copies.push(ret); + } + + return ret; +} + +/** + Creates a clone of the passed object. This function can take just about + any type of object and create a clone of it, including primitive values + (which are not actually cloned because they are immutable). + + If the passed object implements the `clone()` method, then this function + will simply call that method and return the result. + + @method copy + @for Ember + @param {Object} obj The object to clone + @param {Boolean} deep If true, a deep copy of the object is made + @return {Object} The cloned object +*/ +Ember.copy = function(obj, deep) { + // fast paths + if ('object' !== typeof obj || obj===null) return obj; // can't copy primitives + if (Ember.Copyable && Ember.Copyable.detect(obj)) return obj.copy(deep); + return _copy(obj, deep, deep ? [] : null, deep ? [] : null); +}; + +/** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. + + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump + + @method inspect + @for Ember + @param {Object} obj The object you want to inspect. + @return {String} A description of the object +*/ +Ember.inspect = function(obj) { + if (typeof obj !== 'object' || obj === null) { + return obj + ''; + } + + var v, ret = []; + for(var key in obj) { + if (obj.hasOwnProperty(key)) { + v = obj[key]; + if (v === 'toString') { continue; } // ignore useless items + if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; } + ret.push(key + ": " + v); + } + } + return "{" + ret.join(", ") + "}"; +}; + +/** + Compares two objects, returning true if they are logically equal. This is + a deeper comparison than a simple triple equal. For sets it will compare the + internal objects. For any other object that implements `isEqual()` it will + respect that method. + + ```javascript + Ember.isEqual('hello', 'hello'); // true + Ember.isEqual(1, 2); // false + Ember.isEqual([4,2], [4,2]); // false + ``` + + @method isEqual + @for Ember + @param {Object} a first object to compare + @param {Object} b second object to compare + @return {Boolean} +*/ +Ember.isEqual = function(a, b) { + if (a && 'function'===typeof a.isEqual) return a.isEqual(b); + return a === b; +}; + +// Used by Ember.compare +Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [ + 'undefined', + 'null', + 'boolean', + 'number', + 'string', + 'array', + 'object', + 'instance', + 'function', + 'class', + 'date' +]; + +/** + Returns all of the keys defined on an object or hash. This is useful + when inspecting objects for debugging. On browsers that support it, this + uses the native `Object.keys` implementation. + + @method keys + @for Ember + @param {Object} obj + @return {Array} Array containing keys of obj +*/ +Ember.keys = Object.keys; + +if (!Ember.keys || Ember.create.isSimulated) { + Ember.keys = function(obj) { + var ret = []; + for(var key in obj) { + // Prevents browsers that don't respect non-enumerability from + // copying internal Ember properties + if (key.substring(0,2) === '__') continue; + if (key === '_super') continue; + + if (obj.hasOwnProperty(key)) { ret.push(key); } + } + return ret; + }; +} + +// .......................................................... +// ERROR +// + +var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + +/** + A subclass of the JavaScript Error object for use in Ember. + + @class Error + @namespace Ember + @extends Error + @constructor +*/ +Ember.Error = function() { + var tmp = Error.apply(this, arguments); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } +}; + +Ember.Error.prototype = Ember.create(Error.prototype); + +})(); + + + +(function() { +/** + Expose RSVP implementation + + @class RSVP + @namespace Ember + @constructor +*/ +Ember.RSVP = requireModule('rsvp'); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var STRING_DASHERIZE_REGEXP = (/[ _]/g); +var STRING_DASHERIZE_CACHE = {}; +var STRING_DECAMELIZE_REGEXP = (/([a-z])([A-Z])/g); +var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); +var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); +var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); + +/** + Defines the hash of localized strings for the current language. Used by + the `Ember.String.loc()` helper. To localize, add string values to this + hash. + + @property STRINGS + @for Ember + @type Hash +*/ +Ember.STRINGS = {}; + +/** + Defines string helper methods including string formatting and localization. + Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be + added to the `String.prototype` as well. + + @class String + @namespace Ember + @static +*/ +Ember.String = { + + /** + Apply formatting options to the string. This will look for occurrences + of "%@" in your string and substitute them with the arguments you pass into + this method. If you want to control the specific order of replacement, + you can add a number after the key as well to indicate which argument + you want to insert. + + Ordered insertions are most useful when building loc strings where values + you need to insert may appear in different orders. + + ```javascript + "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" + "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" + ``` + + @method fmt + @param {String} str The string to format + @param {Array} formats An array of parameters to interpolate into string. + @return {String} formatted string + */ + fmt: function(str, formats) { + // first, replace any ORDERED replacements. + var idx = 0; // the current index for non-numerical replacements + return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { + argIndex = (argIndex) ? parseInt(argIndex,0) - 1 : idx++ ; + s = formats[argIndex]; + return ((s === null) ? '(null)' : (s === undefined) ? '' : s).toString(); + }) ; + }, + + /** + Formats the passed string, but first looks up the string in the localized + strings hash. This is a convenient way to localize text. See + `Ember.String.fmt()` for more information on formatting. + + Note that it is traditional but not required to prefix localized string + keys with an underscore or other character so you can easily identify + localized strings. + + ```javascript + Ember.STRINGS = { + '_Hello World': 'Bonjour le monde', + '_Hello %@ %@': 'Bonjour %@ %@' + }; + + Ember.String.loc("_Hello World"); // 'Bonjour le monde'; + Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; + ``` + + @method loc + @param {String} str The string to format + @param {Array} formats Optional array of parameters to interpolate into string. + @return {String} formatted string + */ + loc: function(str, formats) { + str = Ember.STRINGS[str] || str; + return Ember.String.fmt(str, formats) ; + }, + + /** + Splits a string into separate units separated by spaces, eliminating any + empty strings in the process. This is a convenience method for split that + is mostly useful when applied to the `String.prototype`. + + ```javascript + Ember.String.w("alpha beta gamma").forEach(function(key) { + console.log(key); + }); + + // > alpha + // > beta + // > gamma + ``` + + @method w + @param {String} str The string to split + @return {String} split string + */ + w: function(str) { return str.split(/\s+/); }, + + /** + Converts a camelized string into all lower case separated by underscores. + + ```javascript + 'innerHTML'.decamelize(); // 'inner_html' + 'action_name'.decamelize(); // 'action_name' + 'css-class-name'.decamelize(); // 'css-class-name' + 'my favorite items'.decamelize(); // 'my favorite items' + ``` + + @method decamelize + @param {String} str The string to decamelize. + @return {String} the decamelized string. + */ + decamelize: function(str) { + return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); + }, + + /** + Replaces underscores or spaces with dashes. + + ```javascript + 'innerHTML'.dasherize(); // 'inner-html' + 'action_name'.dasherize(); // 'action-name' + 'css-class-name'.dasherize(); // 'css-class-name' + 'my favorite items'.dasherize(); // 'my-favorite-items' + ``` + + @method dasherize + @param {String} str The string to dasherize. + @return {String} the dasherized string. + */ + dasherize: function(str) { + var cache = STRING_DASHERIZE_CACHE, + hit = cache.hasOwnProperty(str), + ret; + + if (hit) { + return cache[str]; + } else { + ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); + cache[str] = ret; + } + + return ret; + }, + + /** + Returns the lowerCamelCase form of a string. + + ```javascript + 'innerHTML'.camelize(); // 'innerHTML' + 'action_name'.camelize(); // 'actionName' + 'css-class-name'.camelize(); // 'cssClassName' + 'my favorite items'.camelize(); // 'myFavoriteItems' + 'My Favorite Items'.camelize(); // 'myFavoriteItems' + ``` + + @method camelize + @param {String} str The string to camelize. + @return {String} the camelized string. + */ + camelize: function(str) { + return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { + return chr ? chr.toUpperCase() : ''; + }).replace(/^([A-Z])/, function(match, separator, chr) { + return match.toLowerCase(); + }); + }, + + /** + Returns the UpperCamelCase form of a string. + + ```javascript + 'innerHTML'.classify(); // 'InnerHTML' + 'action_name'.classify(); // 'ActionName' + 'css-class-name'.classify(); // 'CssClassName' + 'my favorite items'.classify(); // 'MyFavoriteItems' + ``` + + @method classify + @param {String} str the string to classify + @return {String} the classified string + */ + classify: function(str) { + var parts = str.split("."), + out = []; + + for (var i=0, l=parts.length; i<l; i++) { + var camelized = Ember.String.camelize(parts[i]); + out.push(camelized.charAt(0).toUpperCase() + camelized.substr(1)); + } + + return out.join("."); + }, + + /** + More general than decamelize. Returns the lower\_case\_and\_underscored + form of a string. + + ```javascript + 'innerHTML'.underscore(); // 'inner_html' + 'action_name'.underscore(); // 'action_name' + 'css-class-name'.underscore(); // 'css_class_name' + 'my favorite items'.underscore(); // 'my_favorite_items' + ``` + + @method underscore + @param {String} str The string to underscore. + @return {String} the underscored string. + */ + underscore: function(str) { + return str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2'). + replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase(); + }, + + /** + Returns the Capitalized form of a string + + ```javascript + 'innerHTML'.capitalize() // 'InnerHTML' + 'action_name'.capitalize() // 'Action_name' + 'css-class-name'.capitalize() // 'Css-class-name' + 'my favorite items'.capitalize() // 'My favorite items' + ``` + + @method capitalize + @param {String} str The string to capitalize. + @return {String} The capitalized string. + */ + capitalize: function(str) { + return str.charAt(0).toUpperCase() + str.substr(1); + } + +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + + +var fmt = Ember.String.fmt, + w = Ember.String.w, + loc = Ember.String.loc, + camelize = Ember.String.camelize, + decamelize = Ember.String.decamelize, + dasherize = Ember.String.dasherize, + underscore = Ember.String.underscore, + capitalize = Ember.String.capitalize, + classify = Ember.String.classify; + +if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + + /** + See `Ember.String.fmt`. + + @method fmt + @for String + */ + String.prototype.fmt = function() { + return fmt(this, arguments); + }; + + /** + See `Ember.String.w`. + + @method w + @for String + */ + String.prototype.w = function() { + return w(this); + }; + + /** + See `Ember.String.loc`. + + @method loc + @for String + */ + String.prototype.loc = function() { + return loc(this, arguments); + }; + + /** + See `Ember.String.camelize`. + + @method camelize + @for String + */ + String.prototype.camelize = function() { + return camelize(this); + }; + + /** + See `Ember.String.decamelize`. + + @method decamelize + @for String + */ + String.prototype.decamelize = function() { + return decamelize(this); + }; + + /** + See `Ember.String.dasherize`. + + @method dasherize + @for String + */ + String.prototype.dasherize = function() { + return dasherize(this); + }; + + /** + See `Ember.String.underscore`. + + @method underscore + @for String + */ + String.prototype.underscore = function() { + return underscore(this); + }; + + /** + See `Ember.String.classify`. + + @method classify + @for String + */ + String.prototype.classify = function() { + return classify(this); + }; + + /** + See `Ember.String.capitalize`. + + @method capitalize + @for String + */ + String.prototype.capitalize = function() { + return capitalize(this); + }; + +} + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var a_slice = Array.prototype.slice; + +if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { + + /** + The `property` extension of Javascript's Function prototype is available + when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is + `true`, which is the default. + + Computed properties allow you to treat a function like a property: + + ```javascript + MyApp.president = Ember.Object.create({ + firstName: "Barack", + lastName: "Obama", + + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // Call this flag to mark the function as a property + }.property() + }); + + MyApp.president.get('fullName'); // "Barack Obama" + ``` + + Treating a function like a property is useful because they can work with + bindings, just like any other property. + + Many computed properties have dependencies on other properties. For + example, in the above example, the `fullName` property depends on + `firstName` and `lastName` to determine its value. You can tell Ember + about these dependencies like this: + + ```javascript + MyApp.president = Ember.Object.create({ + firstName: "Barack", + lastName: "Obama", + + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // Tell Ember.js that this computed property depends on firstName + // and lastName + }.property('firstName', 'lastName') + }); + ``` + + Make sure you list these dependencies so Ember knows when to update + bindings that connect to a computed property. Changing a dependency + will not immediately trigger an update of the computed property, but + will instead clear the cache so that it is updated when the next `get` + is called on the property. + + See `Ember.ComputedProperty`, `Ember.computed`. + + @method property + @for Function + */ + Function.prototype.property = function() { + var ret = Ember.computed(this); + return ret.property.apply(ret, arguments); + }; + + /** + The `observes` extension of Javascript's Function prototype is available + when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is + true, which is the default. + + You can observe property changes simply by adding the `observes` + call to the end of your method declarations in classes that you write. + For example: + + ```javascript + Ember.Object.create({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); + ``` + + See `Ember.Observable.observes`. + + @method observes + @for Function + */ + Function.prototype.observes = function() { + this.__ember_observes__ = a_slice.call(arguments); + return this; + }; + + /** + The `observesBefore` extension of Javascript's Function prototype is + available when `Ember.EXTEND_PROTOTYPES` or + `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default. + + You can get notified when a property changes is about to happen by + by adding the `observesBefore` call to the end of your method + declarations in classes that you write. For example: + + ```javascript + Ember.Object.create({ + valueObserver: function() { + // Executes whenever the "value" property is about to change + }.observesBefore('value') + }); + ``` + + See `Ember.Observable.observesBefore`. + + @method observesBefore + @for Function + */ + Function.prototype.observesBefore = function() { + this.__ember_observesBefore__ = a_slice.call(arguments); + return this; + }; + +} + + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +// .......................................................... +// HELPERS +// + +var get = Ember.get, set = Ember.set; +var a_slice = Array.prototype.slice; +var a_indexOf = Ember.EnumerableUtils.indexOf; + +var contexts = []; + +function popCtx() { + return contexts.length===0 ? {} : contexts.pop(); +} + +function pushCtx(ctx) { + contexts.push(ctx); + return null; +} + +function iter(key, value) { + var valueProvided = arguments.length === 2; + + function i(item) { + var cur = get(item, key); + return valueProvided ? value===cur : !!cur; + } + return i ; +} + +/** + This mixin defines the common interface implemented by enumerable objects + in Ember. Most of these methods follow the standard Array iteration + API defined up to JavaScript 1.8 (excluding language-specific features that + cannot be emulated in older versions of JavaScript). + + This mixin is applied automatically to the Array class on page load, so you + can use any of these methods on simple arrays. If Array already implements + one of these methods, the mixin will not override them. + + ## Writing Your Own Enumerable + + To make your own custom class enumerable, you need two items: + + 1. You must have a length property. This property should change whenever + the number of items in your enumerable object changes. If you using this + with an `Ember.Object` subclass, you should be sure to change the length + property using `set().` + + 2. If you must implement `nextObject().` See documentation. + + Once you have these two methods implement, apply the `Ember.Enumerable` mixin + to your class and you will be able to enumerate the contents of your object + like any other collection. + + ## Using Ember Enumeration with Other Libraries + + Many other libraries provide some kind of iterator or enumeration like + facility. This is often where the most common API conflicts occur. + Ember's API is designed to be as friendly as possible with other + libraries by implementing only methods that mostly correspond to the + JavaScript 1.8 API. + + @class Enumerable + @namespace Ember + @extends Ember.Mixin + @since Ember 0.9 +*/ +Ember.Enumerable = Ember.Mixin.create({ + + // compatibility + isEnumerable: true, + + /** + Implement this method to make your class enumerable. + + This method will be call repeatedly during enumeration. The index value + will always begin with 0 and increment monotonically. You don't have to + rely on the index value to determine what object to return, but you should + always check the value and start from the beginning when you see the + requested index is 0. + + The `previousObject` is the object that was returned from the last call + to `nextObject` for the current iteration. This is a useful way to + manage iteration if you are tracing a linked list, for example. + + Finally the context parameter will always contain a hash you can use as + a "scratchpad" to maintain any other state you need in order to iterate + properly. The context object is reused and is not reset between + iterations so make sure you setup the context with a fresh state whenever + the index parameter is 0. + + Generally iterators will continue to call `nextObject` until the index + reaches the your current length-1. If you run out of data before this + time for some reason, you should simply return undefined. + + The default implementation of this method simply looks up the index. + This works great on any Array-like objects. + + @method nextObject + @param {Number} index the current index of the iteration + @param {Object} previousObject the value returned by the last call to + `nextObject`. + @param {Object} context a context object you can use to maintain state. + @return {Object} the next object in the iteration or undefined + */ + nextObject: Ember.required(Function), + + /** + Helper method returns the first object from a collection. This is usually + used by bindings and other parts of the framework to extract a single + object if the enumerable contains only one item. + + If you override this method, you should implement it so that it will + always return the same value each time it is called. If your enumerable + contains only one object, this method should always return that object. + If your enumerable is empty, this method should return `undefined`. + + ```javascript + var arr = ["a", "b", "c"]; + arr.get('firstObject'); // "a" + + var arr = []; + arr.get('firstObject'); // undefined + ``` + + @property firstObject + @return {Object} the object or undefined + */ + firstObject: Ember.computed(function() { + if (get(this, 'length')===0) return undefined ; + + // handle generic enumerables + var context = popCtx(), ret; + ret = this.nextObject(0, null, context); + pushCtx(context); + return ret ; + }).property('[]'), + + /** + Helper method returns the last object from a collection. If your enumerable + contains only one object, this method should always return that object. + If your enumerable is empty, this method should return `undefined`. + + ```javascript + var arr = ["a", "b", "c"]; + arr.get('lastObject'); // "c" + + var arr = []; + arr.get('lastObject'); // undefined + ``` + + @property lastObject + @return {Object} the last object or undefined + */ + lastObject: Ember.computed(function() { + var len = get(this, 'length'); + if (len===0) return undefined ; + var context = popCtx(), idx=0, cur, last = null; + do { + last = cur; + cur = this.nextObject(idx++, last, context); + } while (cur !== undefined); + pushCtx(context); + return last; + }).property('[]'), + + /** + Returns `true` if the passed object can be found in the receiver. The + default version will iterate through the enumerable until the object + is found. You may want to override this with a more efficient version. + + ```javascript + var arr = ["a", "b", "c"]; + arr.contains("a"); // true + arr.contains("z"); // false + ``` + + @method contains + @param {Object} obj The object to search for. + @return {Boolean} `true` if object is found in enumerable. + */ + contains: function(obj) { + return this.find(function(item) { return item===obj; }) !== undefined; + }, + + /** + Iterates through the enumerable, calling the passed function on each + item. This method corresponds to the `forEach()` method defined in + JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + @method forEach + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Object} receiver + */ + forEach: function(callback, target) { + if (typeof callback !== "function") throw new TypeError() ; + var len = get(this, 'length'), last = null, context = popCtx(); + + if (target === undefined) target = null; + + for(var idx=0;idx<len;idx++) { + var next = this.nextObject(idx, last, context) ; + callback.call(target, next, idx, this); + last = next ; + } + last = null ; + context = pushCtx(context); + return this ; + }, + + /** + Alias for `mapProperty` + + @method getEach + @param {String} key name of the property + @return {Array} The mapped array. + */ + getEach: function(key) { + return this.mapProperty(key); + }, + + /** + Sets the value on the named property for each member. This is more + efficient than using other methods defined on this helper. If the object + implements Ember.Observable, the value will be changed to `set(),` otherwise + it will be set directly. `null` objects are skipped. + + @method setEach + @param {String} key The key to set + @param {Object} value The object to set + @return {Object} receiver + */ + setEach: function(key, value) { + return this.forEach(function(item) { + set(item, key, value); + }); + }, + + /** + Maps all of the items in the enumeration to another value, returning + a new array. This method corresponds to `map()` defined in JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + It should return the mapped value. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + @method map + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} The mapped array. + */ + map: function(callback, target) { + var ret = Ember.A([]); + this.forEach(function(x, idx, i) { + ret[idx] = callback.call(target, x, idx,i); + }); + return ret ; + }, + + /** + Similar to map, this specialized function returns the value of the named + property on all items in the enumeration. + + @method mapProperty + @param {String} key name of the property + @return {Array} The mapped array. + */ + mapProperty: function(key) { + return this.map(function(next) { + return get(next, key); + }); + }, + + /** + Returns an array with all of the items in the enumeration that the passed + function returns true for. This method corresponds to `filter()` defined in + JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + It should return the `true` to include the item in the results, `false` + otherwise. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + @method filter + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A filtered array. + */ + filter: function(callback, target) { + var ret = Ember.A([]); + this.forEach(function(x, idx, i) { + if (callback.call(target, x, idx, i)) ret.push(x); + }); + return ret ; + }, + + /** + Returns an array with all of the items in the enumeration where the passed + function returns false for. This method is the inverse of filter(). + + The callback method you provide should have the following signature (all + parameters are optional): + + function(item, index, enumerable); + + - *item* is the current item in the iteration. + - *index* is the current index in the iteration + - *enumerable* is the enumerable object itself. + + It should return the a falsey value to include the item in the results. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as "this" on the context. This is a good way + to give your iterator function access to the current object. + + @method reject + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A rejected array. + */ + reject: function(callback, target) { + return this.filter(function() { + return !(callback.apply(target, arguments)); + }); + }, + + /** + Returns an array with just the items with the matched property. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. + + @method filterProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} filtered array + */ + filterProperty: function(key, value) { + return this.filter(iter.apply(this, arguments)); + }, + + /** + Returns an array with the items that do not have truthy values for + key. You can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to false. + + @method rejectProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Array} rejected array + */ + rejectProperty: function(key, value) { + var exactValue = function(item) { return get(item, key) === value; }, + hasValue = function(item) { return !!get(item, key); }, + use = (arguments.length === 2 ? exactValue : hasValue); + + return this.reject(use); + }, + + /** + Returns the first item in the array for which the callback returns true. + This method works similar to the `filter()` method defined in JavaScript 1.6 + except that it will stop working on the array once a match is found. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + It should return the `true` to include the item in the results, `false` + otherwise. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + @method find + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Object} Found item or `undefined`. + */ + find: function(callback, target) { + var len = get(this, 'length') ; + if (target === undefined) target = null; + + var last = null, next, found = false, ret ; + var context = popCtx(); + for(var idx=0;idx<len && !found;idx++) { + next = this.nextObject(idx, last, context) ; + if (found = callback.call(target, next, idx, this)) ret = next ; + last = next ; + } + next = last = null ; + context = pushCtx(context); + return ret ; + }, + + /** + Returns the first item with a property matching the passed value. You + can pass an optional second argument with the target value. Otherwise + this will match any property that evaluates to `true`. + + This method works much like the more generic `find()` method. + + @method findProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Object} found item or `undefined` + */ + findProperty: function(key, value) { + return this.find(iter.apply(this, arguments)); + }, + + /** + Returns `true` if the passed function returns true for every item in the + enumeration. This corresponds with the `every()` method in JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + It should return the `true` or `false`. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + Example Usage: + + ```javascript + if (people.every(isEngineer)) { Paychecks.addBigBonus(); } + ``` + + @method every + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Boolean} + */ + every: function(callback, target) { + return !this.find(function(x, idx, i) { + return !callback.call(target, x, idx, i); + }); + }, + + /** + Returns `true` if the passed property resolves to `true` for all items in + the enumerable. This method is often simpler/faster than using a callback. + + @method everyProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} + */ + everyProperty: function(key, value) { + return this.every(iter.apply(this, arguments)); + }, + + + /** + Returns `true` if the passed function returns true for any item in the + enumeration. This corresponds with the `some()` method in JavaScript 1.6. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(item, index, enumerable); + ``` + + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + It should return the `true` to include the item in the results, `false` + otherwise. + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. This is a good way + to give your iterator function access to the current object. + + Usage Example: + + ```javascript + if (people.some(isManager)) { Paychecks.addBiggerBonus(); } + ``` + + @method some + @param {Function} callback The callback to execute + @param {Object} [target] The target object to use + @return {Array} A filtered array. + */ + some: function(callback, target) { + return !!this.find(function(x, idx, i) { + return !!callback.call(target, x, idx, i); + }); + }, + + /** + Returns `true` if the passed property resolves to `true` for any item in + the enumerable. This method is often simpler/faster than using a callback. + + @method someProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` + */ + someProperty: function(key, value) { + return this.some(iter.apply(this, arguments)); + }, + + /** + This will combine the values of the enumerator into a single value. It + is a useful way to collect a summary value from an enumeration. This + corresponds to the `reduce()` method defined in JavaScript 1.8. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(previousValue, item, index, enumerable); + ``` + + - `previousValue` is the value returned by the last call to the iterator. + - `item` is the current item in the iteration. + - `index` is the current index in the iteration. + - `enumerable` is the enumerable object itself. + + Return the new cumulative value. + + In addition to the callback you can also pass an `initialValue`. An error + will be raised if you do not pass an initial value and the enumerator is + empty. + + Note that unlike the other methods, this method does not allow you to + pass a target object to set as this for the callback. It's part of the + spec. Sorry. + + @method reduce + @param {Function} callback The callback to execute + @param {Object} initialValue Initial value for the reduce + @param {String} reducerProperty internal use only. + @return {Object} The reduced value. + */ + reduce: function(callback, initialValue, reducerProperty) { + if (typeof callback !== "function") { throw new TypeError(); } + + var ret = initialValue; + + this.forEach(function(item, i) { + ret = callback.call(null, ret, item, i, this, reducerProperty); + }, this); + + return ret; + }, + + /** + Invokes the named method on every object in the receiver that + implements it. This method corresponds to the implementation in + Prototype 1.6. + + @method invoke + @param {String} methodName the name of the method + @param {Object...} args optional arguments to pass as well. + @return {Array} return values from calling invoke. + */ + invoke: function(methodName) { + var args, ret = Ember.A([]); + if (arguments.length>1) args = a_slice.call(arguments, 1); + + this.forEach(function(x, idx) { + var method = x && x[methodName]; + if ('function' === typeof method) { + ret[idx] = args ? method.apply(x, args) : method.call(x); + } + }, this); + + return ret; + }, + + /** + Simply converts the enumerable into a genuine array. The order is not + guaranteed. Corresponds to the method implemented by Prototype. + + @method toArray + @return {Array} the enumerable as an array. + */ + toArray: function() { + var ret = Ember.A([]); + this.forEach(function(o, idx) { ret[idx] = o; }); + return ret ; + }, + + /** + Returns a copy of the array with all null and undefined elements removed. + + ```javascript + var arr = ["a", null, "c", undefined]; + arr.compact(); // ["a", "c"] + ``` + + @method compact + @return {Array} the array without null and undefined elements. + */ + compact: function() { + return this.filter(function(value) { return value != null; }); + }, + + /** + Returns a new enumerable that excludes the passed value. The default + implementation returns an array regardless of the receiver type unless + the receiver does not contain the value. + + ```javascript + var arr = ["a", "b", "a", "c"]; + arr.without("a"); // ["b", "c"] + ``` + + @method without + @param {Object} value + @return {Ember.Enumerable} + */ + without: function(value) { + if (!this.contains(value)) return this; // nothing to do + var ret = Ember.A([]); + this.forEach(function(k) { + if (k !== value) ret[ret.length] = k; + }) ; + return ret ; + }, + + /** + Returns a new enumerable that contains only unique values. The default + implementation returns an array regardless of the receiver type. + + ```javascript + var arr = ["a", "a", "b", "b"]; + arr.uniq(); // ["a", "b"] + ``` + + @method uniq + @return {Ember.Enumerable} + */ + uniq: function() { + var ret = Ember.A([]); + this.forEach(function(k){ + if (a_indexOf(ret, k)<0) ret.push(k); + }); + return ret; + }, + + /** + This property will trigger anytime the enumerable's content changes. + You can observe this property to be notified of changes to the enumerables + content. + + For plain enumerables, this property is read only. `Ember.Array` overrides + this method. + + @property [] + @type Ember.Array + @return this + */ + '[]': Ember.computed(function(key, value) { + return this; + }), + + // .......................................................... + // ENUMERABLE OBSERVERS + // + + /** + Registers an enumerable observer. Must implement `Ember.EnumerableObserver` + mixin. + + @method addEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this + */ + addEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange', + didChange = (opts && opts.didChange) || 'enumerableDidChange'; + + var hasObservers = get(this, 'hasEnumerableObservers'); + if (!hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers'); + Ember.addListener(this, '@enumerable:before', target, willChange); + Ember.addListener(this, '@enumerable:change', target, didChange); + if (!hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers'); + return this; + }, + + /** + Removes a registered enumerable observer. + + @method removeEnumerableObserver + @param {Object} target + @param {Hash} [opts] + @return this + */ + removeEnumerableObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'enumerableWillChange', + didChange = (opts && opts.didChange) || 'enumerableDidChange'; + + var hasObservers = get(this, 'hasEnumerableObservers'); + if (hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers'); + Ember.removeListener(this, '@enumerable:before', target, willChange); + Ember.removeListener(this, '@enumerable:change', target, didChange); + if (hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers'); + return this; + }, + + /** + Becomes true whenever the array currently has observers watching changes + on the array. + + @property hasEnumerableObservers + @type Boolean + */ + hasEnumerableObservers: Ember.computed(function() { + return Ember.hasListeners(this, '@enumerable:change') || Ember.hasListeners(this, '@enumerable:before'); + }), + + + /** + Invoke this method just before the contents of your enumerable will + change. You can either omit the parameters completely or pass the objects + to be removed or added if available or just a count. + + @method enumerableContentWillChange + @param {Ember.Enumerable|Number} removing An enumerable of the objects to + be removed or the number of items to be removed. + @param {Ember.Enumerable|Number} adding An enumerable of the objects to be + added or the number of items to be added. + @chainable + */ + enumerableContentWillChange: function(removing, adding) { + + var removeCnt, addCnt, hasDelta; + + if ('number' === typeof removing) removeCnt = removing; + else if (removing) removeCnt = get(removing, 'length'); + else removeCnt = removing = -1; + + if ('number' === typeof adding) addCnt = adding; + else if (adding) addCnt = get(adding,'length'); + else addCnt = adding = -1; + + hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; + + if (removing === -1) removing = null; + if (adding === -1) adding = null; + + Ember.propertyWillChange(this, '[]'); + if (hasDelta) Ember.propertyWillChange(this, 'length'); + Ember.sendEvent(this, '@enumerable:before', [this, removing, adding]); + + return this; + }, + + /** + Invoke this method when the contents of your enumerable has changed. + This will notify any observers watching for content changes. If your are + implementing an ordered enumerable (such as an array), also pass the + start and end values where the content changed so that it can be used to + notify range observers. + + @method enumerableContentDidChange + @param {Number} [start] optional start offset for the content change. + For unordered enumerables, you should always pass -1. + @param {Ember.Enumerable|Number} removing An enumerable of the objects to + be removed or the number of items to be removed. + @param {Ember.Enumerable|Number} adding An enumerable of the objects to + be added or the number of items to be added. + @chainable + */ + enumerableContentDidChange: function(removing, adding) { + var removeCnt, addCnt, hasDelta; + + if ('number' === typeof removing) removeCnt = removing; + else if (removing) removeCnt = get(removing, 'length'); + else removeCnt = removing = -1; + + if ('number' === typeof adding) addCnt = adding; + else if (adding) addCnt = get(adding, 'length'); + else addCnt = adding = -1; + + hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; + + if (removing === -1) removing = null; + if (adding === -1) adding = null; + + Ember.sendEvent(this, '@enumerable:change', [this, removing, adding]); + if (hasDelta) Ember.propertyDidChange(this, 'length'); + Ember.propertyDidChange(this, '[]'); + + return this ; + } + +}) ; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +// .......................................................... +// HELPERS +// + +var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.EnumerableUtils.map, cacheFor = Ember.cacheFor; + +// .......................................................... +// ARRAY +// +/** + This module implements Observer-friendly Array-like behavior. This mixin is + picked up by the Array class as well as other controllers, etc. that want to + appear to be arrays. + + Unlike `Ember.Enumerable,` this mixin defines methods specifically for + collections that provide index-ordered access to their contents. When you + are designing code that needs to accept any kind of Array-like object, you + should use these methods instead of Array primitives because these will + properly notify observers of changes to the array. + + Although these methods are efficient, they do add a layer of indirection to + your application so it is a good idea to use them only when you need the + flexibility of using both true JavaScript arrays and "virtual" arrays such + as controllers and collections. + + You can use the methods defined in this module to access and modify array + contents in a KVO-friendly way. You can also be notified whenever the + membership of an array changes by changing the syntax of the property to + `.observes('*myProperty.[]')`. + + To support `Ember.Array` in your own class, you must override two + primitives to use it: `replace()` and `objectAt()`. + + Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` + mixin. All `Ember.Array`-like objects are also enumerable. + + @class Array + @namespace Ember + @extends Ember.Mixin + @uses Ember.Enumerable + @since Ember 0.9.0 +*/ +Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ { + + /** + Your array must support the `length` property. Your replace methods should + set this property whenever it changes. + + @property {Number} length + */ + length: Ember.required(), + + /** + Returns the object at the given `index`. If the given `index` is negative + or is greater or equal than the array length, returns `undefined`. + + This is one of the primitives you must implement to support `Ember.Array`. + If your object supports retrieving the value of an array item using `get()` + (i.e. `myArray.get(0)`), then you do not need to implement this method + yourself. + + ```javascript + var arr = ['a', 'b', 'c', 'd']; + arr.objectAt(0); // "a" + arr.objectAt(3); // "d" + arr.objectAt(-1); // undefined + arr.objectAt(4); // undefined + arr.objectAt(5); // undefined + ``` + + @method objectAt + @param {Number} idx The index of the item to return. + @return {*} item at index or undefined + */ + objectAt: function(idx) { + if ((idx < 0) || (idx>=get(this, 'length'))) return undefined ; + return get(this, idx); + }, + + /** + This returns the objects at the specified indexes, using `objectAt`. + + ```javascript + var arr = ['a', 'b', 'c', 'd']; + arr.objectsAt([0, 1, 2]); // ["a", "b", "c"] + arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] + ``` + + @method objectsAt + @param {Array} indexes An array of indexes of items to return. + @return {Array} + */ + objectsAt: function(indexes) { + var self = this; + return map(indexes, function(idx){ return self.objectAt(idx); }); + }, + + // overrides Ember.Enumerable version + nextObject: function(idx) { + return this.objectAt(idx); + }, + + /** + This is the handler for the special array content property. If you get + this property, it will return this. If you set this property it a new + array, it will replace the current content. + + This property overrides the default property defined in `Ember.Enumerable`. + + @property [] + @return this + */ + '[]': Ember.computed(function(key, value) { + if (value !== undefined) this.replace(0, get(this, 'length'), value) ; + return this ; + }), + + firstObject: Ember.computed(function() { + return this.objectAt(0); + }), + + lastObject: Ember.computed(function() { + return this.objectAt(get(this, 'length')-1); + }), + + // optimized version from Enumerable + contains: function(obj){ + return this.indexOf(obj) >= 0; + }, + + // Add any extra methods to Ember.Array that are native to the built-in Array. + /** + Returns a new array that is a slice of the receiver. This implementation + uses the observable array methods to retrieve the objects for the new + slice. + + ```javascript + var arr = ['red', 'green', 'blue']; + arr.slice(0); // ['red', 'green', 'blue'] + arr.slice(0, 2); // ['red', 'green'] + arr.slice(1, 100); // ['green', 'blue'] + ``` + + @method slice + @param {Integer} beginIndex (Optional) index to begin slicing from. + @param {Integer} endIndex (Optional) index to end the slice at. + @return {Array} New array with specified slice + */ + slice: function(beginIndex, endIndex) { + var ret = Ember.A([]); + var length = get(this, 'length') ; + if (isNone(beginIndex)) beginIndex = 0 ; + if (isNone(endIndex) || (endIndex > length)) endIndex = length ; + + if (beginIndex < 0) beginIndex = length + beginIndex; + if (endIndex < 0) endIndex = length + endIndex; + + while(beginIndex < endIndex) { + ret[ret.length] = this.objectAt(beginIndex++) ; + } + return ret ; + }, + + /** + Returns the index of the given object's first occurrence. + If no `startAt` argument is given, the starting location to + search is 0. If it's negative, will count backward from + the end of the array. Returns -1 if no match is found. + + ```javascript + var arr = ["a", "b", "c", "d", "a"]; + arr.indexOf("a"); // 0 + arr.indexOf("z"); // -1 + arr.indexOf("a", 2); // 4 + arr.indexOf("a", -1); // 4 + arr.indexOf("b", 3); // -1 + arr.indexOf("a", 100); // -1 + ``` + + @method indexOf + @param {Object} object the item to search for + @param {Number} startAt optional starting location to search, default 0 + @return {Number} index or -1 if not found + */ + indexOf: function(object, startAt) { + var idx, len = get(this, 'length'); + + if (startAt === undefined) startAt = 0; + if (startAt < 0) startAt += len; + + for(idx=startAt;idx<len;idx++) { + if (this.objectAt(idx, true) === object) return idx ; + } + return -1; + }, + + /** + Returns the index of the given object's last occurrence. + If no `startAt` argument is given, the search starts from + the last position. If it's negative, will count backward + from the end of the array. Returns -1 if no match is found. + + ```javascript + var arr = ["a", "b", "c", "d", "a"]; + arr.lastIndexOf("a"); // 4 + arr.lastIndexOf("z"); // -1 + arr.lastIndexOf("a", 2); // 0 + arr.lastIndexOf("a", -1); // 4 + arr.lastIndexOf("b", 3); // 1 + arr.lastIndexOf("a", 100); // 4 + ``` + + @method lastIndexOf + @param {Object} object the item to search for + @param {Number} startAt optional starting location to search, default 0 + @return {Number} index or -1 if not found + */ + lastIndexOf: function(object, startAt) { + var idx, len = get(this, 'length'); + + if (startAt === undefined || startAt >= len) startAt = len-1; + if (startAt < 0) startAt += len; + + for(idx=startAt;idx>=0;idx--) { + if (this.objectAt(idx) === object) return idx ; + } + return -1; + }, + + // .......................................................... + // ARRAY OBSERVERS + // + + /** + Adds an array observer to the receiving array. The array observer object + normally must implement two methods: + + * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be + called just before the array is modified. + * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be + called just after the array is modified. + + Both callbacks will be passed the observed object, starting index of the + change as well a a count of the items to be removed and added. You can use + these callbacks to optionally inspect the array during the change, clear + caches, or do any other bookkeeping necessary. + + In addition to passing a target, you can also include an options hash + which you can use to override the method names that will be invoked on the + target. + + @method addArrayObserver + @param {Object} target The observer object. + @param {Hash} opts Optional hash of configuration options including + `willChange` and `didChange` option. + @return {Ember.Array} receiver + */ + addArrayObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'arrayWillChange', + didChange = (opts && opts.didChange) || 'arrayDidChange'; + + var hasObservers = get(this, 'hasArrayObservers'); + if (!hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers'); + Ember.addListener(this, '@array:before', target, willChange); + Ember.addListener(this, '@array:change', target, didChange); + if (!hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers'); + return this; + }, + + /** + Removes an array observer from the object if the observer is current + registered. Calling this method multiple times with the same object will + have no effect. + + @method removeArrayObserver + @param {Object} target The object observing the array. + @param {Hash} opts Optional hash of configuration options including + `willChange` and `didChange` option. + @return {Ember.Array} receiver + */ + removeArrayObserver: function(target, opts) { + var willChange = (opts && opts.willChange) || 'arrayWillChange', + didChange = (opts && opts.didChange) || 'arrayDidChange'; + + var hasObservers = get(this, 'hasArrayObservers'); + if (hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers'); + Ember.removeListener(this, '@array:before', target, willChange); + Ember.removeListener(this, '@array:change', target, didChange); + if (hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers'); + return this; + }, + + /** + Becomes true whenever the array currently has observers watching changes + on the array. + + @property Boolean + */ + hasArrayObservers: Ember.computed(function() { + return Ember.hasListeners(this, '@array:change') || Ember.hasListeners(this, '@array:before'); + }), + + /** + If you are implementing an object that supports `Ember.Array`, call this + method just before the array content changes to notify any observers and + invalidate any related properties. Pass the starting index of the change + as well as a delta of the amounts to change. + + @method arrayContentWillChange + @param {Number} startIdx The starting index in the array that will change. + @param {Number} removeAmt The number of items that will be removed. If you + pass `null` assumes 0 + @param {Number} addAmt The number of items that will be added. If you + pass `null` assumes 0. + @return {Ember.Array} receiver + */ + arrayContentWillChange: function(startIdx, removeAmt, addAmt) { + + // if no args are passed assume everything changes + if (startIdx===undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) removeAmt=-1; + if (addAmt === undefined) addAmt=-1; + } + + // Make sure the @each proxy is set up if anyone is observing @each + if (Ember.isWatching(this, '@each')) { get(this, '@each'); } + + Ember.sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); + + var removing, lim; + if (startIdx>=0 && removeAmt>=0 && get(this, 'hasEnumerableObservers')) { + removing = []; + lim = startIdx+removeAmt; + for(var idx=startIdx;idx<lim;idx++) removing.push(this.objectAt(idx)); + } else { + removing = removeAmt; + } + + this.enumerableContentWillChange(removing, addAmt); + + return this; + }, + + /** + If you are implementing an object that supports `Ember.Array`, call this + method just after the array content changes to notify any observers and + invalidate any related properties. Pass the starting index of the change + as well as a delta of the amounts to change. + + @method arrayContentDidChange + @param {Number} startIdx The starting index in the array that did change. + @param {Number} removeAmt The number of items that were removed. If you + pass `null` assumes 0 + @param {Number} addAmt The number of items that were added. If you + pass `null` assumes 0. + @return {Ember.Array} receiver + */ + arrayContentDidChange: function(startIdx, removeAmt, addAmt) { + + // if no args are passed assume everything changes + if (startIdx===undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) removeAmt=-1; + if (addAmt === undefined) addAmt=-1; + } + + var adding, lim; + if (startIdx>=0 && addAmt>=0 && get(this, 'hasEnumerableObservers')) { + adding = []; + lim = startIdx+addAmt; + for(var idx=startIdx;idx<lim;idx++) adding.push(this.objectAt(idx)); + } else { + adding = addAmt; + } + + this.enumerableContentDidChange(removeAmt, adding); + Ember.sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); + + var length = get(this, 'length'), + cachedFirst = cacheFor(this, 'firstObject'), + cachedLast = cacheFor(this, 'lastObject'); + if (this.objectAt(0) !== cachedFirst) { + Ember.propertyWillChange(this, 'firstObject'); + Ember.propertyDidChange(this, 'firstObject'); + } + if (this.objectAt(length-1) !== cachedLast) { + Ember.propertyWillChange(this, 'lastObject'); + Ember.propertyDidChange(this, 'lastObject'); + } + + return this; + }, + + // .......................................................... + // ENUMERATED PROPERTIES + // + + /** + Returns a special object that can be used to observe individual properties + on the array. Just get an equivalent property on this object and it will + return an enumerable that maps automatically to the named key on the + member objects. + + @property @each + */ + '@each': Ember.computed(function() { + if (!this.__each) this.__each = new Ember.EachProxy(this); + return this.__each; + }) + +}) ; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + +/** + Implements some standard methods for comparing objects. Add this mixin to + any class you create that can compare its instances. + + You should implement the `compare()` method. + + @class Comparable + @namespace Ember + @extends Ember.Mixin + @since Ember 0.9 +*/ +Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{ + + /** + walk like a duck. Indicates that the object can be compared. + + @property isComparable + @type Boolean + @default true + */ + isComparable: true, + + /** + Override to return the result of the comparison of the two parameters. The + compare method should return: + + - `-1` if `a < b` + - `0` if `a == b` + - `1` if `a > b` + + Default implementation raises an exception. + + @method compare + @param a {Object} the first object to compare + @param b {Object} the second object to compare + @return {Integer} the result of the comparison + */ + compare: Ember.required(Function) + +}); + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + + +var get = Ember.get, set = Ember.set; + +/** + Implements some standard methods for copying an object. Add this mixin to + any object you create that can create a copy of itself. This mixin is + added automatically to the built-in array. + + You should generally implement the `copy()` method to return a copy of the + receiver. + + Note that `frozenCopy()` will only work if you also implement + `Ember.Freezable`. + + @class Copyable + @namespace Ember + @extends Ember.Mixin + @since Ember 0.9 +*/ +Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { + + /** + Override to return a copy of the receiver. Default implementation raises + an exception. + + @method copy + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver + */ + copy: Ember.required(Function), + + /** + If the object implements `Ember.Freezable`, then this will return a new + copy if the object is not frozen and the receiver if the object is frozen. + + Raises an exception if you try to call this method on a object that does + not support freezing. + + You should use this method whenever you want a copy of a freezable object + since a freezable object can simply return itself without actually + consuming more memory. + + @method frozenCopy + @return {Object} copy of receiver or receiver + */ + frozenCopy: function() { + if (Ember.Freezable && Ember.Freezable.detect(this)) { + return get(this, 'isFrozen') ? this : this.copy().freeze(); + } else { + throw new Error(Ember.String.fmt("%@ does not support freezing", [this])); + } + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + +var get = Ember.get, set = Ember.set; + +/** + The `Ember.Freezable` mixin implements some basic methods for marking an + object as frozen. Once an object is frozen it should be read only. No changes + may be made the internal state of the object. + + ## Enforcement + + To fully support freezing in your subclass, you must include this mixin and + override any method that might alter any property on the object to instead + raise an exception. You can check the state of an object by checking the + `isFrozen` property. + + Although future versions of JavaScript may support language-level freezing + object objects, that is not the case today. Even if an object is freezable, + it is still technically possible to modify the object, even though it could + break other parts of your application that do not expect a frozen object to + change. It is, therefore, very important that you always respect the + `isFrozen` property on all freezable objects. + + ## Example Usage + + The example below shows a simple object that implement the `Ember.Freezable` + protocol. + + ```javascript + Contact = Ember.Object.extend(Ember.Freezable, { + firstName: null, + lastName: null, + + // swaps the names + swapNames: function() { + if (this.get('isFrozen')) throw Ember.FROZEN_ERROR; + var tmp = this.get('firstName'); + this.set('firstName', this.get('lastName')); + this.set('lastName', tmp); + return this; + } + + }); + + c = Context.create({ firstName: "John", lastName: "Doe" }); + c.swapNames(); // returns c + c.freeze(); + c.swapNames(); // EXCEPTION + ``` + + ## Copying + + Usually the `Ember.Freezable` protocol is implemented in cooperation with the + `Ember.Copyable` protocol, which defines a `frozenCopy()` method that will + return a frozen object, if the object implements this method as well. + + @class Freezable + @namespace Ember + @extends Ember.Mixin + @since Ember 0.9 +*/ +Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { + + /** + Set to `true` when the object is frozen. Use this property to detect + whether your object is frozen or not. + + @property isFrozen + @type Boolean + */ + isFrozen: false, + + /** + Freezes the object. Once this method has been called the object should + no longer allow any properties to be edited. + + @method freeze + @return {Object} receiver + */ + freeze: function() { + if (get(this, 'isFrozen')) return this; + set(this, 'isFrozen', true); + return this; + } + +}); + +Ember.FROZEN_ERROR = "Frozen object cannot be modified."; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var forEach = Ember.EnumerableUtils.forEach; + +/** + This mixin defines the API for modifying generic enumerables. These methods + can be applied to an object regardless of whether it is ordered or + unordered. + + Note that an Enumerable can change even if it does not implement this mixin. + For example, a MappedEnumerable cannot be directly modified but if its + underlying enumerable changes, it will change also. + + ## Adding Objects + + To add an object to an enumerable, use the `addObject()` method. This + method will only add the object to the enumerable if the object is not + already present and is of a type supported by the enumerable. + + ```javascript + set.addObject(contact); + ``` + + ## Removing Objects + + To remove an object from an enumerable, use the `removeObject()` method. This + will only remove the object if it is present in the enumerable, otherwise + this method has no effect. + + ```javascript + set.removeObject(contact); + ``` + + ## Implementing In Your Own Code + + If you are implementing an object and want to support this API, just include + this mixin in your class and implement the required methods. In your unit + tests, be sure to apply the Ember.MutableEnumerableTests to your object. + + @class MutableEnumerable + @namespace Ember + @extends Ember.Mixin + @uses Ember.Enumerable +*/ +Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, { + + /** + __Required.__ You must implement this method to apply this mixin. + + Attempts to add the passed object to the receiver if the object is not + already present in the collection. If the object is present, this method + has no effect. + + If the passed object is of a type not supported by the receiver, + then this method should raise an exception. + + @method addObject + @param {Object} object The object to add to the enumerable. + @return {Object} the passed object + */ + addObject: Ember.required(Function), + + /** + Adds each object in the passed enumerable to the receiver. + + @method addObjects + @param {Ember.Enumerable} objects the objects to add. + @return {Object} receiver + */ + addObjects: function(objects) { + Ember.beginPropertyChanges(this); + forEach(objects, function(obj) { this.addObject(obj); }, this); + Ember.endPropertyChanges(this); + return this; + }, + + /** + __Required.__ You must implement this method to apply this mixin. + + Attempts to remove the passed object from the receiver collection if the + object is present in the collection. If the object is not present, + this method has no effect. + + If the passed object is of a type not supported by the receiver, + then this method should raise an exception. + + @method removeObject + @param {Object} object The object to remove from the enumerable. + @return {Object} the passed object + */ + removeObject: Ember.required(Function), + + + /** + Removes each object in the passed enumerable from the receiver. + + @method removeObjects + @param {Ember.Enumerable} objects the objects to remove + @return {Object} receiver + */ + removeObjects: function(objects) { + Ember.beginPropertyChanges(this); + forEach(objects, function(obj) { this.removeObject(obj); }, this); + Ember.endPropertyChanges(this); + return this; + } + +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ +// .......................................................... +// CONSTANTS +// + +var OUT_OF_RANGE_EXCEPTION = "Index out of range" ; +var EMPTY = []; + +// .......................................................... +// HELPERS +// + +var get = Ember.get, set = Ember.set; + +/** + This mixin defines the API for modifying array-like objects. These methods + can be applied only to a collection that keeps its items in an ordered set. + + Note that an Array can change even if it does not implement this mixin. + For example, one might implement a SparseArray that cannot be directly + modified, but if its underlying enumerable changes, it will change also. + + @class MutableArray + @namespace Ember + @extends Ember.Mixin + @uses Ember.Array + @uses Ember.MutableEnumerable +*/ +Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ { + + /** + __Required.__ You must implement this method to apply this mixin. + + This is one of the primitives you must implement to support `Ember.Array`. + You should replace amt objects started at idx with the objects in the + passed array. You should also call `this.enumerableContentDidChange()` + + @method replace + @param {Number} idx Starting index in the array to replace. If + idx >= length, then append to the end of the array. + @param {Number} amt Number of elements that should be removed from + the array, starting at *idx*. + @param {Array} objects An array of zero or more objects that should be + inserted into the array at *idx* + */ + replace: Ember.required(), + + /** + Remove all elements from self. This is useful if you + want to reuse an existing array without having to recreate it. + + ```javascript + var colors = ["red", "green", "blue"]; + color.length(); // 3 + colors.clear(); // [] + colors.length(); // 0 + ``` + + @method clear + @return {Ember.Array} An empty Array. + */ + clear: function () { + var len = get(this, 'length'); + if (len === 0) return this; + this.replace(0, len, EMPTY); + return this; + }, + + /** + This will use the primitive `replace()` method to insert an object at the + specified index. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] + colors.insertAt(5, "orange"); // Error: Index out of range + ``` + + @method insertAt + @param {Number} idx index of insert the object at. + @param {Object} object object to insert + @return this + */ + insertAt: function(idx, object) { + if (idx > get(this, 'length')) throw new Error(OUT_OF_RANGE_EXCEPTION) ; + this.replace(idx, 0, [object]) ; + return this ; + }, + + /** + Remove an object at the specified index using the `replace()` primitive + method. You can pass either a single index, or a start and a length. + + If you pass a start and length that is beyond the + length this method will throw an `Ember.OUT_OF_RANGE_EXCEPTION` + + ```javascript + var colors = ["red", "green", "blue", "yellow", "orange"]; + colors.removeAt(0); // ["green", "blue", "yellow", "orange"] + colors.removeAt(2, 2); // ["green", "blue"] + colors.removeAt(4, 2); // Error: Index out of range + ``` + + @method removeAt + @param {Number} start index, start of range + @param {Number} len length of passing range + @return {Object} receiver + */ + removeAt: function(start, len) { + if ('number' === typeof start) { + + if ((start < 0) || (start >= get(this, 'length'))) { + throw new Error(OUT_OF_RANGE_EXCEPTION); + } + + // fast case + if (len === undefined) len = 1; + this.replace(start, len, EMPTY); + } + + return this ; + }, + + /** + Push the object onto the end of the array. Works just like `push()` but it + is KVO-compliant. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.pushObject("black"); // ["red", "green", "blue", "black"] + colors.pushObject(["yellow", "orange"]); // ["red", "green", "blue", "black", ["yellow", "orange"]] + ``` + + @method pushObject + @param {*} obj object to push + @return {*} the same obj passed as param + */ + pushObject: function(obj) { + this.insertAt(get(this, 'length'), obj) ; + return obj ; + }, + + /** + Add the objects in the passed numerable to the end of the array. Defers + notifying observers of the change until all objects are added. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.pushObjects("black"); // ["red", "green", "blue", "black"] + colors.pushObjects(["yellow", "orange"]); // ["red", "green", "blue", "black", "yellow", "orange"] + ``` + + @method pushObjects + @param {Ember.Enumerable} objects the objects to add + @return {Ember.Array} receiver + */ + pushObjects: function(objects) { + this.replace(get(this, 'length'), 0, objects); + return this; + }, + + /** + Pop object from array or nil if none are left. Works just like `pop()` but + it is KVO-compliant. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.popObject(); // "blue" + console.log(colors); // ["red", "green"] + ``` + + @method popObject + @return object + */ + popObject: function() { + var len = get(this, 'length') ; + if (len === 0) return null ; + + var ret = this.objectAt(len-1) ; + this.removeAt(len-1, 1) ; + return ret ; + }, + + /** + Shift an object from start of array or nil if none are left. Works just + like `shift()` but it is KVO-compliant. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.shiftObject(); // "red" + console.log(colors); // ["green", "blue"] + ``` + + @method shiftObject + @return object + */ + shiftObject: function() { + if (get(this, 'length') === 0) return null ; + var ret = this.objectAt(0) ; + this.removeAt(0) ; + return ret ; + }, + + /** + Unshift an object to start of array. Works just like `unshift()` but it is + KVO-compliant. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.unshiftObject("yellow"); // ["yellow", "red", "green", "blue"] + colors.unshiftObject(["black", "white"]); // [["black", "white"], "yellow", "red", "green", "blue"] + ``` + + @method unshiftObject + @param {*} obj object to unshift + @return {*} the same obj passed as param + */ + unshiftObject: function(obj) { + this.insertAt(0, obj) ; + return obj ; + }, + + /** + Adds the named objects to the beginning of the array. Defers notifying + observers until all objects have been added. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.unshiftObjects(["black", "white"]); // ["black", "white", "red", "green", "blue"] + colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function + ``` + + @method unshiftObjects + @param {Ember.Enumerable} objects the objects to add + @return {Ember.Array} receiver + */ + unshiftObjects: function(objects) { + this.replace(0, 0, objects); + return this; + }, + + /** + Reverse objects in the array. Works just like `reverse()` but it is + KVO-compliant. + + @method reverseObjects + @return {Ember.Array} receiver + */ + reverseObjects: function() { + var len = get(this, 'length'); + if (len === 0) return this; + var objects = this.toArray().reverse(); + this.replace(0, len, objects); + return this; + }, + + /** + Replace all the the receiver's content with content of the argument. + If argument is an empty array receiver will be cleared. + + ```javascript + var colors = ["red", "green", "blue"]; + colors.setObjects(["black", "white"]); // ["black", "white"] + colors.setObjects([]); // [] + ``` + + @method setObjects + @param {Ember.Array} objects array whose content will be used for replacing + the content of the receiver + @return {Ember.Array} receiver with the new content + */ + setObjects: function(objects) { + if (objects.length === 0) return this.clear(); + + var len = get(this, 'length'); + this.replace(0, len, objects); + return this; + }, + + // .......................................................... + // IMPLEMENT Ember.MutableEnumerable + // + + removeObject: function(obj) { + var loc = get(this, 'length') || 0; + while(--loc >= 0) { + var curObject = this.objectAt(loc) ; + if (curObject === obj) this.removeAt(loc) ; + } + return this ; + }, + + addObject: function(obj) { + if (!this.contains(obj)) this.pushObject(obj); + return this ; + } + +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, set = Ember.set; + +/** + ## Overview + + This mixin provides properties and property observing functionality, core + features of the Ember object model. + + Properties and observers allow one object to observe changes to a + property on another object. This is one of the fundamental ways that + models, controllers and views communicate with each other in an Ember + application. + + Any object that has this mixin applied can be used in observer + operations. That includes `Ember.Object` and most objects you will + interact with as you write your Ember application. + + Note that you will not generally apply this mixin to classes yourself, + but you will use the features provided by this module frequently, so it + is important to understand how to use it. + + ## Using `get()` and `set()` + + Because of Ember's support for bindings and observers, you will always + access properties using the get method, and set properties using the + set method. This allows the observing objects to be notified and + computed properties to be handled properly. + + More documentation about `get` and `set` are below. + + ## Observing Property Changes + + You typically observe property changes simply by adding the `observes` + call to the end of your method declarations in classes that you write. + For example: + + ```javascript + Ember.Object.extend({ + valueObserver: function() { + // Executes whenever the "value" property changes + }.observes('value') + }); + ``` + + Although this is the most common way to add an observer, this capability + is actually built into the `Ember.Object` class on top of two methods + defined in this mixin: `addObserver` and `removeObserver`. You can use + these two methods to add and remove observers yourself if you need to + do so at runtime. + + To add an observer for a property, call: + + ```javascript + object.addObserver('propertyKey', targetObject, targetAction) + ``` + + This will call the `targetAction` method on the `targetObject` whenever + the value of the `propertyKey` changes. + + Note that if `propertyKey` is a computed property, the observer will be + called when any of the property dependencies are changed, even if the + resulting value of the computed property is unchanged. This is necessary + because computed properties are not computed until `get` is called. + + @class Observable + @namespace Ember + @extends Ember.Mixin +*/ +Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { + + /** + Retrieves the value of a property from the object. + + This method is usually similar to using `object[keyName]` or `object.keyName`, + however it supports both computed properties and the unknownProperty + handler. + + Because `get` unifies the syntax for accessing all these kinds + of properties, it can make many refactorings easier, such as replacing a + simple property with a computed property, or vice versa. + + ### Computed Properties + + Computed properties are methods defined with the `property` modifier + declared at the end, such as: + + ```javascript + fullName: function() { + return this.getEach('firstName', 'lastName').compact().join(' '); + }.property('firstName', 'lastName') + ``` + + When you call `get` on a computed property, the function will be + called and the return value will be returned instead of the function + itself. + + ### Unknown Properties + + Likewise, if you try to call `get` on a property whose value is + `undefined`, the `unknownProperty()` method will be called on the object. + If this method returns any value other than `undefined`, it will be returned + instead. This allows you to implement "virtual" properties that are + not defined upfront. + + @method get + @param {String} keyName The property to retrieve + @return {Object} The property value or undefined. + */ + get: function(keyName) { + return get(this, keyName); + }, + + /** + To get multiple properties at once, call `getProperties` + with a list of strings or an array: + + ```javascript + record.getProperties('firstName', 'lastName', 'zipCode'); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` + + is equivalent to: + + ```javascript + record.getProperties(['firstName', 'lastName', 'zipCode']); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } + ``` + + @method getProperties + @param {String...|Array} list of keys to get + @return {Hash} + */ + getProperties: function() { + var ret = {}; + var propertyNames = arguments; + if (arguments.length === 1 && Ember.typeOf(arguments[0]) === 'array') { + propertyNames = arguments[0]; + } + for(var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = get(this, propertyNames[i]); + } + return ret; + }, + + /** + Sets the provided key or path to the value. + + This method is generally very similar to calling `object[key] = value` or + `object.key = value`, except that it provides support for computed + properties, the `unknownProperty()` method and property observers. + + ### Computed Properties + + If you try to set a value on a key that has a computed property handler + defined (see the `get()` method for an example), then `set()` will call + that method, passing both the value and key instead of simply changing + the value itself. This is useful for those times when you need to + implement a property that is composed of one or more member + properties. + + ### Unknown Properties + + If you try to set a value on a key that is undefined in the target + object, then the `unknownProperty()` handler will be called instead. This + gives you an opportunity to implement complex "virtual" properties that + are not predefined on the object. If `unknownProperty()` returns + undefined, then `set()` will simply set the value on the object. + + ### Property Observers + + In addition to changing the property, `set()` will also register a property + change with the object. Unless you have placed this call inside of a + `beginPropertyChanges()` and `endPropertyChanges(),` any "local" observers + (i.e. observer methods declared on the same object), will be called + immediately. Any "remote" observers (i.e. observer methods declared on + another object) will be placed in a queue and called at a later time in a + coalesced manner. + + ### Chaining + + In addition to property changes, `set()` returns the value of the object + itself so you can do chaining like this: + + ```javascript + record.set('firstName', 'Charles').set('lastName', 'Jolley'); + ``` + + @method set + @param {String} keyName The property to set + @param {Object} value The value to set or `null`. + @return {Ember.Observable} + */ + set: function(keyName, value) { + set(this, keyName, value); + return this; + }, + + /** + To set multiple properties at once, call `setProperties` + with a Hash: + + ```javascript + record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); + ``` + + @method setProperties + @param {Hash} hash the hash of keys and values to set + @return {Ember.Observable} + */ + setProperties: function(hash) { + return Ember.setProperties(this, hash); + }, + + /** + Begins a grouping of property changes. + + You can use this method to group property changes so that notifications + will not be sent until the changes are finished. If you plan to make a + large number of changes to an object at one time, you should call this + method at the beginning of the changes to begin deferring change + notifications. When you are done making changes, call + `endPropertyChanges()` to deliver the deferred change notifications and end + deferring. + + @method beginPropertyChanges + @return {Ember.Observable} + */ + beginPropertyChanges: function() { + Ember.beginPropertyChanges(); + return this; + }, + + /** + Ends a grouping of property changes. + + You can use this method to group property changes so that notifications + will not be sent until the changes are finished. If you plan to make a + large number of changes to an object at one time, you should call + `beginPropertyChanges()` at the beginning of the changes to defer change + notifications. When you are done making changes, call this method to + deliver the deferred change notifications and end deferring. + + @method endPropertyChanges + @return {Ember.Observable} + */ + endPropertyChanges: function() { + Ember.endPropertyChanges(); + return this; + }, + + /** + Notify the observer system that a property is about to change. + + Sometimes you need to change a value directly or indirectly without + actually calling `get()` or `set()` on it. In this case, you can use this + method and `propertyDidChange()` instead. Calling these two methods + together will notify all observers that the property has potentially + changed value. + + Note that you must always call `propertyWillChange` and `propertyDidChange` + as a pair. If you do not, it may get the property change groups out of + order and cause notifications to be delivered more often than you would + like. + + @method propertyWillChange + @param {String} keyName The property key that is about to change. + @return {Ember.Observable} + */ + propertyWillChange: function(keyName){ + Ember.propertyWillChange(this, keyName); + return this; + }, + + /** + Notify the observer system that a property has just changed. + + Sometimes you need to change a value directly or indirectly without + actually calling `get()` or `set()` on it. In this case, you can use this + method and `propertyWillChange()` instead. Calling these two methods + together will notify all observers that the property has potentially + changed value. + + Note that you must always call `propertyWillChange` and `propertyDidChange` + as a pair. If you do not, it may get the property change groups out of + order and cause notifications to be delivered more often than you would + like. + + @method propertyDidChange + @param {String} keyName The property key that has just changed. + @return {Ember.Observable} + */ + propertyDidChange: function(keyName) { + Ember.propertyDidChange(this, keyName); + return this; + }, + + /** + Convenience method to call `propertyWillChange` and `propertyDidChange` in + succession. + + @method notifyPropertyChange + @param {String} keyName The property key to be notified about. + @return {Ember.Observable} + */ + notifyPropertyChange: function(keyName) { + this.propertyWillChange(keyName); + this.propertyDidChange(keyName); + return this; + }, + + addBeforeObserver: function(key, target, method) { + Ember.addBeforeObserver(this, key, target, method); + }, + + /** + Adds an observer on a property. + + This is the core method used to register an observer for a property. + + Once you call this method, any time the key's value is set, your observer + will be notified. Note that the observers are triggered any time the + value is set, regardless of whether it has actually changed. Your + observer should be prepared to handle that. + + You can also pass an optional context parameter to this method. The + context will be passed to your observer method whenever it is triggered. + Note that if you add the same target/method pair on a key multiple times + with different context parameters, your observer will only be called once + with the last context you passed. + + ### Observer Methods + + Observer methods you pass should generally have the following signature if + you do not pass a `context` parameter: + + ```javascript + fooDidChange: function(sender, key, value, rev) { }; + ``` + + The sender is the object that changed. The key is the property that + changes. The value property is currently reserved and unused. The rev + is the last property revision of the object when it changed, which you can + use to detect if the key value has really changed or not. + + If you pass a `context` parameter, the context will be passed before the + revision like so: + + ```javascript + fooDidChange: function(sender, key, value, context, rev) { }; + ``` + + Usually you will not need the value, context or revision parameters at + the end. In this case, it is common to write observer methods that take + only a sender and key value as parameters or, if you aren't interested in + any of these values, to write an observer that has no parameters at all. + + @method addObserver + @param {String} key The key to observer + @param {Object} target The target object to invoke + @param {String|Function} method The method to invoke. + @return {Ember.Object} self + */ + addObserver: function(key, target, method) { + Ember.addObserver(this, key, target, method); + }, + + /** + Remove an observer you have previously registered on this object. Pass + the same key, target, and method you passed to `addObserver()` and your + target will no longer receive notifications. + + @method removeObserver + @param {String} key The key to observer + @param {Object} target The target object to invoke + @param {String|Function} method The method to invoke. + @return {Ember.Observable} receiver + */ + removeObserver: function(key, target, method) { + Ember.removeObserver(this, key, target, method); + }, + + /** + Returns `true` if the object currently has observers registered for a + particular key. You can use this method to potentially defer performing + an expensive action until someone begins observing a particular property + on the object. + + @method hasObserverFor + @param {String} key Key to check + @return {Boolean} + */ + hasObserverFor: function(key) { + return Ember.hasListeners(this, key+':change'); + }, + + /** + @deprecated + @method getPath + @param {String} path The property path to retrieve + @return {Object} The property value or undefined. + */ + getPath: function(path) { + Ember.deprecate("getPath is deprecated since get now supports paths"); + return this.get(path); + }, + + /** + @deprecated + @method setPath + @param {String} path The path to the property that will be set + @param {Object} value The value to set or `null`. + @return {Ember.Observable} + */ + setPath: function(path, value) { + Ember.deprecate("setPath is deprecated since set now supports paths"); + return this.set(path, value); + }, + + /** + Retrieves the value of a property, or a default value in the case that the + property returns `undefined`. + + ```javascript + person.getWithDefault('lastName', 'Doe'); + ``` + + @method getWithDefault + @param {String} keyName The name of the property to retrieve + @param {Object} defaultValue The value to return if the property value is undefined + @return {Object} The property value or the defaultValue. + */ + getWithDefault: function(keyName, defaultValue) { + return Ember.getWithDefault(this, keyName, defaultValue); + }, + + /** + Set the value of a property to the current value plus some amount. + + ```javascript + person.incrementProperty('age'); + team.incrementProperty('score', 2); + ``` + + @method incrementProperty + @param {String} keyName The name of the property to increment + @param {Number} increment The amount to increment by. Defaults to 1 + @return {Number} The new property value + */ + incrementProperty: function(keyName, increment) { + if (Ember.isNone(increment)) { increment = 1; } + set(this, keyName, (get(this, keyName) || 0)+increment); + return get(this, keyName); + }, + + /** + Set the value of a property to the current value minus some amount. + + ```javascript + player.decrementProperty('lives'); + orc.decrementProperty('health', 5); + ``` + + @method decrementProperty + @param {String} keyName The name of the property to decrement + @param {Number} decrement The amount to decrement by. Defaults to 1 + @return {Number} The new property value + */ + decrementProperty: function(keyName, decrement) { + if (Ember.isNone(decrement)) { decrement = 1; } + set(this, keyName, (get(this, keyName) || 0)-decrement); + return get(this, keyName); + }, + + /** + Set the value of a boolean property to the opposite of it's + current value. + + ```javascript + starship.toggleProperty('warpDriveEngaged'); + ``` + + @method toggleProperty + @param {String} keyName The name of the property to toggle + @return {Object} The new property value + */ + toggleProperty: function(keyName) { + set(this, keyName, !get(this, keyName)); + return get(this, keyName); + }, + + /** + Returns the cached value of a computed property, if it exists. + This allows you to inspect the value of a computed property + without accidentally invoking it if it is intended to be + generated lazily. + + @method cacheFor + @param {String} keyName + @return {Object} The cached value of the computed property, if any + */ + cacheFor: function(keyName) { + return Ember.cacheFor(this, keyName); + }, + + // intended for debugging purposes + observersForKey: function(keyName) { + return Ember.observersFor(this, keyName); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, set = Ember.set; + +/** +`Ember.TargetActionSupport` is a mixin that can be included in a class +to add a `triggerAction` method with semantics similar to the Handlebars +`{{action}}` helper. In normal Ember usage, the `{{action}}` helper is +usually the best choice. This mixin is most often useful when you are +doing more complex event handling in View objects. + +See also `Ember.ViewTargetActionSupport`, which has +view-aware defaults for target and actionContext. + +@class TargetActionSupport +@namespace Ember +@extends Ember.Mixin +*/ +Ember.TargetActionSupport = Ember.Mixin.create({ + target: null, + action: null, + actionContext: null, + + targetObject: Ember.computed(function() { + var target = get(this, 'target'); + + if (Ember.typeOf(target) === "string") { + var value = get(this, target); + if (value === undefined) { value = get(Ember.lookup, target); } + return value; + } else { + return target; + } + }).property('target'), + + actionContextObject: Ember.computed(function() { + var actionContext = get(this, 'actionContext'); + + if (Ember.typeOf(actionContext) === "string") { + var value = get(this, actionContext); + if (value === undefined) { value = get(Ember.lookup, actionContext); } + return value; + } else { + return actionContext; + } + }).property('actionContext'), + + /** + Send an "action" with an "actionContext" to a "target". The action, actionContext + and target will be retrieved from properties of the object. For example: + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { + target: Ember.computed.alias('controller'), + action: 'save', + actionContext: Ember.computed.alias('context'), + click: function(){ + this.triggerAction(); // Sends the `save` action, along with the current context + // to the current controller + } + }); + ``` + + The `target`, `action`, and `actionContext` can be provided as properties of + an optional object argument to `triggerAction` as well. + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { + click: function(){ + this.triggerAction({ + action: 'save', + target: this.get('controller'), + actionContext: this.get('context'), + }); // Sends the `save` action, along with the current context + // to the current controller + } + }); + ``` + + The `actionContext` defaults to the object you mixing `TargetActionSupport` into. + But `target` and `action` must be specified either as properties or with the argument + to `triggerAction`, or a combination: + + ```javascript + App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { + target: Ember.computed.alias('controller'), + click: function(){ + this.triggerAction({ + action: 'save' + }); // Sends the `save` action, along with a reference to `this`, + // to the current controller + } + }); + ``` + + @method triggerAction + @param opts {Hash} (optional, with the optional keys action, target and/or actionContext) + @return {Boolean} true if the action was sent successfully and did not return false + */ + triggerAction: function(opts) { + opts = opts || {}; + var action = opts['action'] || get(this, 'action'), + target = opts['target'] || get(this, 'targetObject'), + actionContext = opts['actionContext'] || get(this, 'actionContextObject') || this; + + if (target && action) { + var ret; + + if (target.send) { + ret = target.send.apply(target, [action, actionContext]); + } else { + Ember.assert("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function'); + ret = target[action].apply(target, [actionContext]); + } + + if (ret !== false) ret = true; + + return ret; + } else { + return false; + } + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +/** + This mixin allows for Ember objects to subscribe to and emit events. + + ```javascript + App.Person = Ember.Object.extend(Ember.Evented, { + greet: function() { + // ... + this.trigger('greet'); + } + }); + + var person = App.Person.create(); + + person.on('greet', function() { + console.log('Our person has greeted'); + }); + + person.greet(); + + // outputs: 'Our person has greeted' + ``` + + You can also chain multiple event subscriptions: + + ```javascript + person.on('greet', function() { + console.log('Our person has greeted'); + }).one('greet', function() { + console.log('Offer one-time special'); + }).off('event', this, forgetThis); + ``` + + @class Evented + @namespace Ember + @extends Ember.Mixin + */ +Ember.Evented = Ember.Mixin.create({ + + /** + Subscribes to a named event with given function. + + ```javascript + person.on('didLoad', function() { + // fired once the person has loaded + }); + ``` + + An optional target can be passed in as the 2nd argument that will + be set as the "this" for the callback. This is a good way to give your + function access to the object triggering the event. When the target + parameter is used the callback becomes the third argument. + + @method on + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function} method The callback to execute + @return this + */ + on: function(name, target, method) { + Ember.addListener(this, name, target, method); + return this; + }, + + /** + Subscribes a function to a named event and then cancels the subscription + after the first time the event is triggered. It is good to use ``one`` when + you only care about the first time an event has taken place. + + This function takes an optional 2nd argument that will become the "this" + value for the callback. If this argument is passed then the 3rd argument + becomes the function. + + @method one + @param {String} name The name of the event + @param {Object} [target] The "this" binding for the callback + @param {Function} method The callback to execute + @return this + */ + one: function(name, target, method) { + if (!method) { + method = target; + target = null; + } + + Ember.addListener(this, name, target, method, true); + return this; + }, + + /** + Triggers a named event for the object. Any additional arguments + will be passed as parameters to the functions that are subscribed to the + event. + + ```javascript + person.on('didEat', function(food) { + console.log('person ate some ' + food); + }); + + person.trigger('didEat', 'broccoli'); + + // outputs: person ate some broccoli + ``` + @method trigger + @param {String} name The name of the event + @param {Object...} args Optional arguments to pass on + */ + trigger: function(name) { + var args = [], i, l; + for (i = 1, l = arguments.length; i < l; i++) { + args.push(arguments[i]); + } + Ember.sendEvent(this, name, args); + }, + + fire: function(name) { + Ember.deprecate("Ember.Evented#fire() has been deprecated in favor of trigger() for compatibility with jQuery. It will be removed in 1.0. Please update your code to call trigger() instead."); + this.trigger.apply(this, arguments); + }, + + /** + Cancels subscription for give name, target, and method. + + @method off + @param {String} name The name of the event + @param {Object} target The target of the subscription + @param {Function} method The function of the subscription + @return this + */ + off: function(name, target, method) { + Ember.removeListener(this, name, target, method); + return this; + }, + + /** + Checks to see if object has any subscriptions for named event. + + @method has + @param {String} name The name of the event + @return {Boolean} does the object have a subscription for event + */ + has: function(name) { + return Ember.hasListeners(this, name); + } +}); + +})(); + + + +(function() { +var RSVP = requireModule("rsvp"); + +RSVP.configure('async', function(callback, binding) { + Ember.run.schedule('actions', binding, callback); +}); + +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get; + +/** + @class Deferred + @namespace Ember + @extends Ember.Mixin + */ +Ember.DeferredMixin = Ember.Mixin.create({ + /** + Add handlers to be called when the Deferred object is resolved or rejected. + + @method then + @param {Function} doneCallback a callback function to be called when done + @param {Function} failCallback a callback function to be called when failed + */ + then: function(resolve, reject) { + var deferred, promise, entity; + + entity = this; + deferred = get(this, '_deferred'); + promise = deferred.promise; + + function fulfillmentHandler(fulfillment) { + if (fulfillment === promise) { + return resolve(entity); + } else { + return resolve(fulfillment); + } + } + + return promise.then(resolve && fulfillmentHandler, reject); + }, + + /** + Resolve a Deferred object and call any `doneCallbacks` with the given args. + + @method resolve + */ + resolve: function(value) { + var deferred, promise; + + deferred = get(this, '_deferred'); + promise = deferred.promise; + + if (value === this){ + deferred.resolve(promise); + } else { + deferred.resolve(value); + } + }, + + /** + Reject a Deferred object and call any `failCallbacks` with the given args. + + @method reject + */ + reject: function(value) { + get(this, '_deferred').reject(value); + }, + + _deferred: Ember.computed(function() { + return RSVP.defer(); + }) +}); + + +})(); + + + +(function() { + +})(); + + + +(function() { +Ember.Container = requireModule('container'); +Ember.Container.set = Ember.set; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + +// NOTE: this object should never be included directly. Instead use Ember. +// Ember.Object. We only define this separately so that Ember.Set can depend on it + + +var set = Ember.set, get = Ember.get, + o_create = Ember.create, + o_defineProperty = Ember.platform.defineProperty, + GUID_KEY = Ember.GUID_KEY, + guidFor = Ember.guidFor, + generateGuid = Ember.generateGuid, + meta = Ember.meta, + rewatch = Ember.rewatch, + finishChains = Ember.finishChains, + destroy = Ember.destroy, + schedule = Ember.run.schedule, + Mixin = Ember.Mixin, + applyMixin = Mixin._apply, + finishPartial = Mixin.finishPartial, + reopen = Mixin.prototype.reopen, + MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, + indexOf = Ember.EnumerableUtils.indexOf; + +var undefinedDescriptor = { + configurable: true, + writable: true, + enumerable: false, + value: undefined +}; + +function makeCtor() { + + // Note: avoid accessing any properties on the object since it makes the + // method a lot faster. This is glue code so we want it to be as fast as + // possible. + + var wasApplied = false, initMixins, initProperties; + + var Class = function() { + if (!wasApplied) { + Class.proto(); // prepare prototype... + } + o_defineProperty(this, GUID_KEY, undefinedDescriptor); + o_defineProperty(this, '_super', undefinedDescriptor); + var m = meta(this); + m.proto = this; + if (initMixins) { + // capture locally so we can clear the closed over variable + var mixins = initMixins; + initMixins = null; + this.reopen.apply(this, mixins); + } + if (initProperties) { + // capture locally so we can clear the closed over variable + var props = initProperties; + initProperties = null; + + var concatenatedProperties = this.concatenatedProperties; + + for (var i = 0, l = props.length; i < l; i++) { + var properties = props[i]; + + Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin)); + + for (var keyName in properties) { + if (!properties.hasOwnProperty(keyName)) { continue; } + + var value = properties[keyName], + IS_BINDING = Ember.IS_BINDING; + + if (IS_BINDING.test(keyName)) { + var bindings = m.bindings; + if (!bindings) { + bindings = m.bindings = {}; + } else if (!m.hasOwnProperty('bindings')) { + bindings = m.bindings = o_create(m.bindings); + } + bindings[keyName] = value; + } + + var desc = m.descs[keyName]; + + Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty)); + Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1)); + + if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) { + var baseValue = this[keyName]; + + if (baseValue) { + if ('function' === typeof baseValue.concat) { + value = baseValue.concat(value); + } else { + value = Ember.makeArray(baseValue).concat(value); + } + } else { + value = Ember.makeArray(value); + } + } + + if (desc) { + desc.set(this, keyName, value); + } else { + if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { + this.setUnknownProperty(keyName, value); + } else if (MANDATORY_SETTER) { + Ember.defineProperty(this, keyName, null, value); // setup mandatory setter + } else { + this[keyName] = value; + } + } + } + } + } + finishPartial(this, m); + delete m.proto; + finishChains(this); + this.init.apply(this, arguments); + }; + + Class.toString = Mixin.prototype.toString; + Class.willReopen = function() { + if (wasApplied) { + Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); + } + + wasApplied = false; + }; + Class._initMixins = function(args) { initMixins = args; }; + Class._initProperties = function(args) { initProperties = args; }; + + Class.proto = function() { + var superclass = Class.superclass; + if (superclass) { superclass.proto(); } + + if (!wasApplied) { + wasApplied = true; + Class.PrototypeMixin.applyPartial(Class.prototype); + rewatch(Class.prototype); + } + + return this.prototype; + }; + + return Class; + +} + +var CoreObject = makeCtor(); +CoreObject.toString = function() { return "Ember.CoreObject"; }; + +CoreObject.PrototypeMixin = Mixin.create({ + reopen: function() { + applyMixin(this, arguments, true); + return this; + }, + + isInstance: true, + + /** + An overridable method called when objects are instantiated. By default, + does nothing unless it is overridden during class definition. + + Example: + + ```javascript + App.Person = Ember.Object.extend({ + init: function() { + this._super(); + alert('Name is ' + this.get('name')); + } + }); + + var steve = App.Person.create({ + name: "Steve" + }); + + // alerts 'Name is Steve'. + ``` + + NOTE: If you do override `init` for a framework class like `Ember.View` or + `Ember.ArrayController`, be sure to call `this._super()` in your + `init` declaration! If you don't, Ember may not have an opportunity to + do important setup work, and you'll see strange behavior in your + application. + + @method init + */ + init: function() {}, + + /** + Defines the properties that will be concatenated from the superclass + (instead of overridden). + + By default, when you extend an Ember class a property defined in + the subclass overrides a property with the same name that is defined + in the superclass. However, there are some cases where it is preferable + to build up a property's value by combining the superclass' property + value with the subclass' value. An example of this in use within Ember + is the `classNames` property of `Ember.View`. + + Here is some sample code showing the difference between a concatenated + property and a normal one: + + ```javascript + App.BarView = Ember.View.extend({ + someNonConcatenatedProperty: ['bar'], + classNames: ['bar'] + }); + + App.FooBarView = App.BarView.extend({ + someNonConcatenatedProperty: ['foo'], + classNames: ['foo'], + }); + + var fooBarView = App.FooBarView.create(); + fooBarView.get('someNonConcatenatedProperty'); // ['foo'] + fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] + ``` + + This behavior extends to object creation as well. Continuing the + above example: + + ```javascript + var view = App.FooBarView.create({ + someNonConcatenatedProperty: ['baz'], + classNames: ['baz'] + }) + view.get('someNonConcatenatedProperty'); // ['baz'] + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` + Adding a single property that is not an array will just add it in the array: + + ```javascript + var view = App.FooBarView.create({ + classNames: 'baz' + }) + view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] + ``` + + Using the `concatenatedProperties` property, we can tell to Ember that mix + the content of the properties. + + In `Ember.View` the `classNameBindings` and `attributeBindings` properties + are also concatenated, in addition to `classNames`. + + This feature is available for you to use throughout the Ember object model, + although typical app developers are likely to use it infrequently. + + @property concatenatedProperties + @type Array + @default null + */ + concatenatedProperties: null, + + /** + Destroyed object property flag. + + if this property is `true` the observers and bindings were already + removed by the effect of calling the `destroy()` method. + + @property isDestroyed + @default false + */ + isDestroyed: false, + + /** + Destruction scheduled flag. The `destroy()` method has been called. + + The object stays intact until the end of the run loop at which point + the `isDestroyed` flag is set. + + @property isDestroying + @default false + */ + isDestroying: false, + + /** + Destroys an object by setting the `isDestroyed` flag and removing its + metadata, which effectively destroys observers and bindings. + + If you try to set a property on a destroyed object, an exception will be + raised. + + Note that destruction is scheduled for the end of the run loop and does not + happen immediately. It will set an isDestroying flag immediately. + + @method destroy + @return {Ember.Object} receiver + */ + destroy: function() { + if (this.isDestroying) { return; } + this.isDestroying = true; + + schedule('actions', this, this.willDestroy); + schedule('destroy', this, this._scheduledDestroy); + return this; + }, + + /** + Override to implement teardown. + */ + willDestroy: Ember.K, + + /** + @private + + Invoked by the run loop to actually destroy the object. This is + scheduled for execution by the `destroy` method. + + @method _scheduledDestroy + */ + _scheduledDestroy: function() { + if (this.isDestroyed) { return; } + destroy(this); + this.isDestroyed = true; + }, + + bind: function(to, from) { + if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); } + from.to(to).connect(this); + return from; + }, + + /** + Returns a string representation which attempts to provide more information + than Javascript's `toString` typically does, in a generic way for all Ember + objects. + + App.Person = Em.Object.extend() + person = App.Person.create() + person.toString() //=> "<App.Person:ember1024>" + + If the object's class is not defined on an Ember namespace, it will + indicate it is a subclass of the registered superclass: + + Student = App.Person.extend() + student = Student.create() + student.toString() //=> "<(subclass of App.Person):ember1025>" + + If the method `toStringExtension` is defined, its return value will be + included in the output. + + App.Teacher = App.Person.extend({ + toStringExtension: function(){ + return this.get('fullName'); + } + }); + teacher = App.Teacher.create() + teacher.toString(); //=> "<App.Teacher:ember1026:Tom Dale>" + + @method toString + @return {String} string representation + */ + toString: function toString() { + var hasToStringExtension = typeof this.toStringExtension === 'function', + extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; + var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; + this.toString = makeToString(ret); + return ret; + } +}); + +CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + +function makeToString(ret) { + return function() { return ret; }; +} + +if (Ember.config.overridePrototypeMixin) { + Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); +} + +CoreObject.__super__ = null; + +var ClassMixin = Mixin.create({ + + ClassMixin: Ember.required(), + + PrototypeMixin: Ember.required(), + + isClass: true, + + isMethod: false, + + extend: function() { + var Class = makeCtor(), proto; + Class.ClassMixin = Mixin.create(this.ClassMixin); + Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); + + Class.ClassMixin.ownerConstructor = Class; + Class.PrototypeMixin.ownerConstructor = Class; + + reopen.apply(Class.PrototypeMixin, arguments); + + Class.superclass = this; + Class.__super__ = this.prototype; + + proto = Class.prototype = o_create(this.prototype); + proto.constructor = Class; + generateGuid(proto, 'ember'); + meta(proto).proto = proto; // this will disable observers on prototype + + Class.ClassMixin.apply(Class); + return Class; + }, + + createWithMixins: function() { + var C = this; + if (arguments.length>0) { this._initMixins(arguments); } + return new C(); + }, + + create: function() { + var C = this; + if (arguments.length>0) { this._initProperties(arguments); } + return new C(); + }, + + reopen: function() { + this.willReopen(); + reopen.apply(this.PrototypeMixin, arguments); + return this; + }, + + reopenClass: function() { + reopen.apply(this.ClassMixin, arguments); + applyMixin(this, arguments, false); + return this; + }, + + detect: function(obj) { + if ('function' !== typeof obj) { return false; } + while(obj) { + if (obj===this) { return true; } + obj = obj.superclass; + } + return false; + }, + + detectInstance: function(obj) { + return obj instanceof this; + }, + + /** + In some cases, you may want to annotate computed properties with additional + metadata about how they function or what values they operate on. For + example, computed property functions may close over variables that are then + no longer available for introspection. + + You can pass a hash of these values to a computed property like this: + + ```javascript + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` + + Once you've done this, you can retrieve the values saved to the computed + property from your class like this: + + ```javascript + MyClass.metaForProperty('person'); + ``` + + This will return the original hash that was passed to `meta()`. + + @method metaForProperty + @param key {String} property name + */ + metaForProperty: function(key) { + var desc = meta(this.proto(), false).descs[key]; + + Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty); + return desc._meta || {}; + }, + + /** + Iterate over each computed property for the class, passing its name + and any associated metadata (see `metaForProperty`) to the callback. + + @method eachComputedProperty + @param {Function} callback + @param {Object} binding + */ + eachComputedProperty: function(callback, binding) { + var proto = this.proto(), + descs = meta(proto).descs, + empty = {}, + property; + + for (var name in descs) { + property = descs[name]; + + if (property instanceof Ember.ComputedProperty) { + callback.call(binding || this, name, property._meta || empty); + } + } + } + +}); + +ClassMixin.ownerConstructor = CoreObject; + +if (Ember.config.overrideClassMixin) { + Ember.config.overrideClassMixin(ClassMixin); +} + +CoreObject.ClassMixin = ClassMixin; +ClassMixin.apply(CoreObject); + +/** + @class CoreObject + @namespace Ember +*/ +Ember.CoreObject = CoreObject; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +/** + `Ember.Object` is the main base class for all Ember objects. It is a subclass + of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, + see the documentation for each of these. + + @class Object + @namespace Ember + @extends Ember.CoreObject + @uses Ember.Observable +*/ +Ember.Object = Ember.CoreObject.extend(Ember.Observable); +Ember.Object.toString = function() { return "Ember.Object"; }; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, indexOf = Ember.ArrayPolyfills.indexOf; + +/** + A Namespace is an object usually used to contain other objects or methods + such as an application or framework. Create a namespace anytime you want + to define one of these new containers. + + # Example Usage + + ```javascript + MyFramework = Ember.Namespace.create({ + VERSION: '1.0.0' + }); + ``` + + @class Namespace + @namespace Ember + @extends Ember.Object +*/ +var Namespace = Ember.Namespace = Ember.Object.extend({ + isNamespace: true, + + init: function() { + Ember.Namespace.NAMESPACES.push(this); + Ember.Namespace.PROCESSED = false; + }, + + toString: function() { + var name = get(this, 'name'); + if (name) { return name; } + + findNamespaces(); + return this[Ember.GUID_KEY+'_name']; + }, + + nameClasses: function() { + processNamespace([this.toString()], this, {}); + }, + + destroy: function() { + var namespaces = Ember.Namespace.NAMESPACES; + Ember.lookup[this.toString()] = undefined; + namespaces.splice(indexOf.call(namespaces, this), 1); + this._super(); + } +}); + +Namespace.reopenClass({ + NAMESPACES: [Ember], + NAMESPACES_BY_ID: {}, + PROCESSED: false, + processAll: processAllNamespaces, + byName: function(name) { + if (!Ember.BOOTED) { + processAllNamespaces(); + } + + return NAMESPACES_BY_ID[name]; + } +}); + +var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; + +var hasOwnProp = ({}).hasOwnProperty, + guidFor = Ember.guidFor; + +function processNamespace(paths, root, seen) { + var idx = paths.length; + + NAMESPACES_BY_ID[paths.join('.')] = root; + + // Loop over all of the keys in the namespace, looking for classes + for(var key in root) { + if (!hasOwnProp.call(root, key)) { continue; } + var obj = root[key]; + + // If we are processing the `Ember` namespace, for example, the + // `paths` will start with `["Ember"]`. Every iteration through + // the loop will update the **second** element of this list with + // the key, so processing `Ember.View` will make the Array + // `['Ember', 'View']`. + paths[idx] = key; + + // If we have found an unprocessed class + if (obj && obj.toString === classToString) { + // Replace the class' `toString` with the dot-separated path + // and set its `NAME_KEY` + obj.toString = makeToString(paths.join('.')); + obj[NAME_KEY] = paths.join('.'); + + // Support nested namespaces + } else if (obj && obj.isNamespace) { + // Skip aliased namespaces + if (seen[guidFor(obj)]) { continue; } + seen[guidFor(obj)] = true; + + // Process the child namespace + processNamespace(paths, obj, seen); + } + } + + paths.length = idx; // cut out last item +} + +function findNamespaces() { + var Namespace = Ember.Namespace, lookup = Ember.lookup, obj, isNamespace; + + if (Namespace.PROCESSED) { return; } + + for (var prop in lookup) { + // These don't raise exceptions but can cause warnings + if (prop === "parent" || prop === "top" || prop === "frameElement" || prop === "webkitStorageInfo") { continue; } + + // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox. + // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage + if (prop === "globalStorage" && lookup.StorageList && lookup.globalStorage instanceof lookup.StorageList) { continue; } + // Unfortunately, some versions of IE don't support window.hasOwnProperty + if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } + + // At times we are not allowed to access certain properties for security reasons. + // There are also times where even if we can access them, we are not allowed to access their properties. + try { + obj = Ember.lookup[prop]; + isNamespace = obj && obj.isNamespace; + } catch (e) { + continue; + } + + if (isNamespace) { + Ember.deprecate("Namespaces should not begin with lowercase.", /^[A-Z]/.test(prop)); + obj[NAME_KEY] = prop; + } + } +} + +var NAME_KEY = Ember.NAME_KEY = Ember.GUID_KEY + '_name'; + +function superClassString(mixin) { + var superclass = mixin.superclass; + if (superclass) { + if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } + else { return superClassString(superclass); } + } else { + return; + } +} + +function classToString() { + if (!Ember.BOOTED && !this[NAME_KEY]) { + processAllNamespaces(); + } + + var ret; + + if (this[NAME_KEY]) { + ret = this[NAME_KEY]; + } else { + var str = superClassString(this); + if (str) { + ret = "(subclass of " + str + ")"; + } else { + ret = "(unknown mixin)"; + } + this.toString = makeToString(ret); + } + + return ret; +} + +function processAllNamespaces() { + var unprocessedNamespaces = !Namespace.PROCESSED, + unprocessedMixins = Ember.anyUnprocessedMixins; + + if (unprocessedNamespaces) { + findNamespaces(); + Namespace.PROCESSED = true; + } + + if (unprocessedNamespaces || unprocessedMixins) { + var namespaces = Namespace.NAMESPACES, namespace; + for (var i=0, l=namespaces.length; i<l; i++) { + namespace = namespaces[i]; + processNamespace([namespace.toString()], namespace, {}); + } + + Ember.anyUnprocessedMixins = false; + } +} + +function makeToString(ret) { + return function() { return ret; }; +} + +Ember.Mixin.prototype.toString = classToString; + +})(); + + + +(function() { +Ember.Application = Ember.Namespace.extend(); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var OUT_OF_RANGE_EXCEPTION = "Index out of range"; +var EMPTY = []; + +var get = Ember.get, set = Ember.set; + +/** + An ArrayProxy wraps any other object that implements `Ember.Array` and/or + `Ember.MutableArray,` forwarding all requests. This makes it very useful for + a number of binding use cases or other cases where being able to swap + out the underlying array is useful. + + A simple example of usage: + + ```javascript + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); + + ap.get('firstObject'); // 'dog' + ap.set('content', ['amoeba', 'paramecium']); + ap.get('firstObject'); // 'amoeba' + ``` + + This class can also be useful as a layer to transform the contents of + an array, as they are accessed. This can be done by overriding + `objectAtContent`: + + ```javascript + var pets = ['dog', 'cat', 'fish']; + var ap = Ember.ArrayProxy.create({ + content: Ember.A(pets), + objectAtContent: function(idx) { + return this.get('content').objectAt(idx).toUpperCase(); + } + }); + + ap.get('firstObject'); // . 'DOG' + ``` + + @class ArrayProxy + @namespace Ember + @extends Ember.Object + @uses Ember.MutableArray +*/ +Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ { + + /** + The content array. Must be an object that implements `Ember.Array` and/or + `Ember.MutableArray.` + + @property content + @type Ember.Array + */ + content: null, + + /** + The array that the proxy pretends to be. In the default `ArrayProxy` + implementation, this and `content` are the same. Subclasses of `ArrayProxy` + can override this property to provide things like sorting and filtering. + + @property arrangedContent + */ + arrangedContent: Ember.computed.alias('content'), + + /** + Should actually retrieve the object at the specified index from the + content. You can override this method in subclasses to transform the + content item to something new. + + This method will only be called if content is non-`null`. + + @method objectAtContent + @param {Number} idx The index to retrieve. + @return {Object} the value or undefined if none found + */ + objectAtContent: function(idx) { + return get(this, 'arrangedContent').objectAt(idx); + }, + + /** + Should actually replace the specified objects on the content array. + You can override this method in subclasses to transform the content item + into something new. + + This method will only be called if content is non-`null`. + + @method replaceContent + @param {Number} idx The starting index + @param {Number} amt The number of items to remove from the content. + @param {Array} objects Optional array of objects to insert or null if no + objects. + @return {void} + */ + replaceContent: function(idx, amt, objects) { + get(this, 'content').replace(idx, amt, objects); + }, + + /** + @private + + Invoked when the content property is about to change. Notifies observers that the + entire array content will change. + + @method _contentWillChange + */ + _contentWillChange: Ember.beforeObserver(function() { + this._teardownContent(); + }, 'content'), + + _teardownContent: function() { + var content = get(this, 'content'); + + if (content) { + content.removeArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + } + }, + + contentArrayWillChange: Ember.K, + contentArrayDidChange: Ember.K, + + /** + @private + + Invoked when the content property changes. Notifies observers that the + entire array content has changed. + + @method _contentDidChange + */ + _contentDidChange: Ember.observer(function() { + var content = get(this, 'content'); + + Ember.assert("Can't set ArrayProxy's content to itself", content !== this); + + this._setupContent(); + }, 'content'), + + _setupContent: function() { + var content = get(this, 'content'); + + if (content) { + content.addArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + } + }, + + _arrangedContentWillChange: Ember.beforeObserver(function() { + var arrangedContent = get(this, 'arrangedContent'), + len = arrangedContent ? get(arrangedContent, 'length') : 0; + + this.arrangedContentArrayWillChange(this, 0, len, undefined); + this.arrangedContentWillChange(this); + + this._teardownArrangedContent(arrangedContent); + }, 'arrangedContent'), + + _arrangedContentDidChange: Ember.observer(function() { + var arrangedContent = get(this, 'arrangedContent'), + len = arrangedContent ? get(arrangedContent, 'length') : 0; + + Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); + + this._setupArrangedContent(); + + this.arrangedContentDidChange(this); + this.arrangedContentArrayDidChange(this, 0, undefined, len); + }, 'arrangedContent'), + + _setupArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); + + if (arrangedContent) { + arrangedContent.addArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); + } + }, + + _teardownArrangedContent: function() { + var arrangedContent = get(this, 'arrangedContent'); + + if (arrangedContent) { + arrangedContent.removeArrayObserver(this, { + willChange: 'arrangedContentArrayWillChange', + didChange: 'arrangedContentArrayDidChange' + }); + } + }, + + arrangedContentWillChange: Ember.K, + arrangedContentDidChange: Ember.K, + + objectAt: function(idx) { + return get(this, 'content') && this.objectAtContent(idx); + }, + + length: Ember.computed(function() { + var arrangedContent = get(this, 'arrangedContent'); + return arrangedContent ? get(arrangedContent, 'length') : 0; + // No dependencies since Enumerable notifies length of change + }), + + _replace: function(idx, amt, objects) { + var content = get(this, 'content'); + Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content); + if (content) this.replaceContent(idx, amt, objects); + return this; + }, + + replace: function() { + if (get(this, 'arrangedContent') === get(this, 'content')) { + this._replace.apply(this, arguments); + } else { + throw new Ember.Error("Using replace on an arranged ArrayProxy is not allowed."); + } + }, + + _insertAt: function(idx, object) { + if (idx > get(this, 'content.length')) throw new Error(OUT_OF_RANGE_EXCEPTION); + this._replace(idx, 0, [object]); + return this; + }, + + insertAt: function(idx, object) { + if (get(this, 'arrangedContent') === get(this, 'content')) { + return this._insertAt(idx, object); + } else { + throw new Ember.Error("Using insertAt on an arranged ArrayProxy is not allowed."); + } + }, + + removeAt: function(start, len) { + if ('number' === typeof start) { + var content = get(this, 'content'), + arrangedContent = get(this, 'arrangedContent'), + indices = [], i; + + if ((start < 0) || (start >= get(this, 'length'))) { + throw new Error(OUT_OF_RANGE_EXCEPTION); + } + + if (len === undefined) len = 1; + + // Get a list of indices in original content to remove + for (i=start; i<start+len; i++) { + // Use arrangedContent here so we avoid confusion with objects transformed by objectAtContent + indices.push(content.indexOf(arrangedContent.objectAt(i))); + } + + // Replace in reverse order since indices will change + indices.sort(function(a,b) { return b - a; }); + + Ember.beginPropertyChanges(); + for (i=0; i<indices.length; i++) { + this._replace(indices[i], 1, EMPTY); + } + Ember.endPropertyChanges(); + } + + return this ; + }, + + pushObject: function(obj) { + this._insertAt(get(this, 'content.length'), obj) ; + return obj ; + }, + + pushObjects: function(objects) { + this._replace(get(this, 'length'), 0, objects); + return this; + }, + + setObjects: function(objects) { + if (objects.length === 0) return this.clear(); + + var len = get(this, 'length'); + this._replace(0, len, objects); + return this; + }, + + unshiftObject: function(obj) { + this._insertAt(0, obj) ; + return obj ; + }, + + unshiftObjects: function(objects) { + this._replace(0, 0, objects); + return this; + }, + + slice: function() { + var arr = this.toArray(); + return arr.slice.apply(arr, arguments); + }, + + arrangedContentArrayWillChange: function(item, idx, removedCnt, addedCnt) { + this.arrayContentWillChange(idx, removedCnt, addedCnt); + }, + + arrangedContentArrayDidChange: function(item, idx, removedCnt, addedCnt) { + this.arrayContentDidChange(idx, removedCnt, addedCnt); + }, + + init: function() { + this._super(); + this._setupContent(); + this._setupArrangedContent(); + }, + + willDestroy: function() { + this._teardownArrangedContent(); + this._teardownContent(); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, + set = Ember.set, + fmt = Ember.String.fmt, + addBeforeObserver = Ember.addBeforeObserver, + addObserver = Ember.addObserver, + removeBeforeObserver = Ember.removeBeforeObserver, + removeObserver = Ember.removeObserver, + propertyWillChange = Ember.propertyWillChange, + propertyDidChange = Ember.propertyDidChange; + +function contentPropertyWillChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyWillChange(this, key); +} + +function contentPropertyDidChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyDidChange(this, key); +} + +/** + `Ember.ObjectProxy` forwards all properties not defined by the proxy itself + to a proxied `content` object. + + ```javascript + object = Ember.Object.create({ + name: 'Foo' + }); + + proxy = Ember.ObjectProxy.create({ + content: object + }); + + // Access and change existing properties + proxy.get('name') // 'Foo' + proxy.set('name', 'Bar'); + object.get('name') // 'Bar' + + // Create new 'description' property on `object` + proxy.set('description', 'Foo is a whizboo baz'); + object.get('description') // 'Foo is a whizboo baz' + ``` + + While `content` is unset, setting a property to be delegated will throw an + Error. + + ```javascript + proxy = Ember.ObjectProxy.create({ + content: null, + flag: null + }); + proxy.set('flag', true); + proxy.get('flag'); // true + proxy.get('foo'); // undefined + proxy.set('foo', 'data'); // throws Error + ``` + + Delegated properties can be bound to and will change when content is updated. + + Computed properties on the proxy itself can depend on delegated properties. + + ```javascript + ProxyWithComputedProperty = Ember.ObjectProxy.extend({ + fullName: function () { + var firstName = this.get('firstName'), + lastName = this.get('lastName'); + if (firstName && lastName) { + return firstName + ' ' + lastName; + } + return firstName || lastName; + }.property('firstName', 'lastName') + }); + + proxy = ProxyWithComputedProperty.create(); + + proxy.get('fullName'); // undefined + proxy.set('content', { + firstName: 'Tom', lastName: 'Dale' + }); // triggers property change for fullName on proxy + + proxy.get('fullName'); // 'Tom Dale' + ``` + + @class ObjectProxy + @namespace Ember + @extends Ember.Object +*/ +Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype */ { + /** + The object whose properties will be forwarded. + + @property content + @type Ember.Object + @default null + */ + content: null, + _contentDidChange: Ember.observer(function() { + Ember.assert("Can't set ObjectProxy's content to itself", this.get('content') !== this); + }, 'content'), + + isTruthy: Ember.computed.bool('content'), + + _debugContainerKey: null, + + willWatchProperty: function (key) { + var contentKey = 'content.' + key; + addBeforeObserver(this, contentKey, null, contentPropertyWillChange); + addObserver(this, contentKey, null, contentPropertyDidChange); + }, + + didUnwatchProperty: function (key) { + var contentKey = 'content.' + key; + removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); + removeObserver(this, contentKey, null, contentPropertyDidChange); + }, + + unknownProperty: function (key) { + var content = get(this, 'content'); + if (content) { + return get(content, key); + } + }, + + setUnknownProperty: function (key, value) { + var content = get(this, 'content'); + Ember.assert(fmt("Cannot delegate set('%@', %@) to the 'content' property of object proxy %@: its 'content' is undefined.", [key, value, this]), content); + return set(content, key, value); + } +}); + +Ember.ObjectProxy.reopenClass({ + create: function () { + var mixin, prototype, i, l, properties, keyName; + if (arguments.length) { + prototype = this.proto(); + for (i = 0, l = arguments.length; i < l; i++) { + properties = arguments[i]; + for (keyName in properties) { + if (!properties.hasOwnProperty(keyName) || keyName in prototype) { continue; } + if (!mixin) mixin = {}; + mixin[keyName] = null; + } + } + if (mixin) this._initMixins([mixin]); + } + return this._super.apply(this, arguments); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + +var set = Ember.set, get = Ember.get, guidFor = Ember.guidFor; +var forEach = Ember.EnumerableUtils.forEach; + +var EachArray = Ember.Object.extend(Ember.Array, { + + init: function(content, keyName, owner) { + this._super(); + this._keyName = keyName; + this._owner = owner; + this._content = content; + }, + + objectAt: function(idx) { + var item = this._content.objectAt(idx); + return item && get(item, this._keyName); + }, + + length: Ember.computed(function() { + var content = this._content; + return content ? get(content, 'length') : 0; + }) + +}); + +var IS_OBSERVER = /^.+:(before|change)$/; + +function addObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects, guid; + if (!objects) objects = proxy._objects = {}; + + while(--loc>=idx) { + var item = content.objectAt(loc); + if (item) { + Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); + Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange'); + + // keep track of the index each item was found at so we can map + // it back when the obj changes. + guid = guidFor(item); + if (!objects[guid]) objects[guid] = []; + objects[guid].push(loc); + } + } +} + +function removeObserverForContentKey(content, keyName, proxy, idx, loc) { + var objects = proxy._objects; + if (!objects) objects = proxy._objects = {}; + var indicies, guid; + + while(--loc>=idx) { + var item = content.objectAt(loc); + if (item) { + Ember.removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); + Ember.removeObserver(item, keyName, proxy, 'contentKeyDidChange'); + + guid = guidFor(item); + indicies = objects[guid]; + indicies[indicies.indexOf(loc)] = null; + } + } +} + +/** + This is the object instance returned when you get the `@each` property on an + array. It uses the unknownProperty handler to automatically create + EachArray instances for property names. + + @private + @class EachProxy + @namespace Ember + @extends Ember.Object +*/ +Ember.EachProxy = Ember.Object.extend({ + + init: function(content) { + this._super(); + this._content = content; + content.addArrayObserver(this); + + // in case someone is already observing some keys make sure they are + // added + forEach(Ember.watchedEvents(this), function(eventName) { + this.didAddListener(eventName); + }, this); + }, + + /** + You can directly access mapped properties by simply requesting them. + The `unknownProperty` handler will generate an EachArray of each item. + + @method unknownProperty + @param keyName {String} + @param value {*} + */ + unknownProperty: function(keyName, value) { + var ret; + ret = new EachArray(this._content, keyName, this); + Ember.defineProperty(this, keyName, null, ret); + this.beginObservingContentKey(keyName); + return ret; + }, + + // .......................................................... + // ARRAY CHANGES + // Invokes whenever the content array itself changes. + + arrayWillChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys, key, lim; + + lim = removedCnt>0 ? idx+removedCnt : -1; + Ember.beginPropertyChanges(this); + + for(key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } + + if (lim>0) removeObserverForContentKey(content, key, this, idx, lim); + + Ember.propertyWillChange(this, key); + } + + Ember.propertyWillChange(this._content, '@each'); + Ember.endPropertyChanges(this); + }, + + arrayDidChange: function(content, idx, removedCnt, addedCnt) { + var keys = this._keys, key, lim; + + lim = addedCnt>0 ? idx+addedCnt : -1; + Ember.beginPropertyChanges(this); + + for(key in keys) { + if (!keys.hasOwnProperty(key)) { continue; } + + if (lim>0) addObserverForContentKey(content, key, this, idx, lim); + + Ember.propertyDidChange(this, key); + } + + Ember.propertyDidChange(this._content, '@each'); + Ember.endPropertyChanges(this); + }, + + // .......................................................... + // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS + // Start monitoring keys based on who is listening... + + didAddListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.beginObservingContentKey(eventName.slice(0, -7)); + } + }, + + didRemoveListener: function(eventName) { + if (IS_OBSERVER.test(eventName)) { + this.stopObservingContentKey(eventName.slice(0, -7)); + } + }, + + // .......................................................... + // CONTENT KEY OBSERVING + // Actual watch keys on the source content. + + beginObservingContentKey: function(keyName) { + var keys = this._keys; + if (!keys) keys = this._keys = {}; + if (!keys[keyName]) { + keys[keyName] = 1; + var content = this._content, + len = get(content, 'length'); + addObserverForContentKey(content, keyName, this, 0, len); + } else { + keys[keyName]++; + } + }, + + stopObservingContentKey: function(keyName) { + var keys = this._keys; + if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { + var content = this._content, + len = get(content, 'length'); + removeObserverForContentKey(content, keyName, this, 0, len); + } + }, + + contentKeyWillChange: function(obj, keyName) { + Ember.propertyWillChange(this, keyName); + }, + + contentKeyDidChange: function(obj, keyName) { + Ember.propertyDidChange(this, keyName); + } + +}); + + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + + +var get = Ember.get, set = Ember.set; + +// Add Ember.Array to Array.prototype. Remove methods with native +// implementations and supply some more optimized versions of generic methods +// because they are so common. +var NativeArray = Ember.Mixin.create(Ember.MutableArray, Ember.Observable, Ember.Copyable, { + + // because length is a built-in property we need to know to just get the + // original property. + get: function(key) { + if (key==='length') return this.length; + else if ('number' === typeof key) return this[key]; + else return this._super(key); + }, + + objectAt: function(idx) { + return this[idx]; + }, + + // primitive for array support. + replace: function(idx, amt, objects) { + + if (this.isFrozen) throw Ember.FROZEN_ERROR ; + + // if we replaced exactly the same number of items, then pass only the + // replaced range. Otherwise, pass the full remaining array length + // since everything has shifted + var len = objects ? get(objects, 'length') : 0; + this.arrayContentWillChange(idx, amt, len); + + if (!objects || objects.length === 0) { + this.splice(idx, amt) ; + } else { + var args = [idx, amt].concat(objects) ; + this.splice.apply(this,args) ; + } + + this.arrayContentDidChange(idx, amt, len); + return this ; + }, + + // If you ask for an unknown property, then try to collect the value + // from member items. + unknownProperty: function(key, value) { + var ret;// = this.reducedProperty(key, value) ; + if ((value !== undefined) && ret === undefined) { + ret = this[key] = value; + } + return ret ; + }, + + // If browser did not implement indexOf natively, then override with + // specialized version + indexOf: function(object, startAt) { + var idx, len = this.length; + + if (startAt === undefined) startAt = 0; + else startAt = (startAt < 0) ? Math.ceil(startAt) : Math.floor(startAt); + if (startAt < 0) startAt += len; + + for(idx=startAt;idx<len;idx++) { + if (this[idx] === object) return idx ; + } + return -1; + }, + + lastIndexOf: function(object, startAt) { + var idx, len = this.length; + + if (startAt === undefined) startAt = len-1; + else startAt = (startAt < 0) ? Math.ceil(startAt) : Math.floor(startAt); + if (startAt < 0) startAt += len; + + for(idx=startAt;idx>=0;idx--) { + if (this[idx] === object) return idx ; + } + return -1; + }, + + copy: function(deep) { + if (deep) { + return this.map(function(item){ return Ember.copy(item, true); }); + } + + return this.slice(); + } +}); + +// Remove any methods implemented natively so we don't override them +var ignore = ['length']; +Ember.EnumerableUtils.forEach(NativeArray.keys(), function(methodName) { + if (Array.prototype[methodName]) ignore.push(methodName); +}); + +if (ignore.length>0) { + NativeArray = NativeArray.without.apply(NativeArray, ignore); +} + +/** + The NativeArray mixin contains the properties needed to to make the native + Array support Ember.MutableArray and all of its dependent APIs. Unless you + have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` set to + false, this will be applied automatically. Otherwise you can apply the mixin + at anytime by calling `Ember.NativeArray.activate`. + + @class NativeArray + @namespace Ember + @extends Ember.Mixin + @uses Ember.MutableArray + @uses Ember.Observable + @uses Ember.Copyable +*/ +Ember.NativeArray = NativeArray; + +/** + Creates an `Ember.NativeArray` from an Array like object. + Does not modify the original object. + + @method A + @for Ember + @return {Ember.NativeArray} +*/ +Ember.A = function(arr){ + if (arr === undefined) { arr = []; } + return Ember.Array.detect(arr) ? arr : Ember.NativeArray.apply(arr); +}; + +/** + Activates the mixin on the Array.prototype if not already applied. Calling + this method more than once is safe. + + @method activate + @for Ember.NativeArray + @static + @return {void} +*/ +Ember.NativeArray.activate = function() { + NativeArray.apply(Array.prototype); + + Ember.A = function(arr) { return arr || []; }; +}; + +if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { + Ember.NativeArray.activate(); +} + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, isNone = Ember.isNone, fmt = Ember.String.fmt; + +/** + An unordered collection of objects. + + A Set works a bit like an array except that its items are not ordered. You + can create a set to efficiently test for membership for an object. You can + also iterate through a set just like an array, even accessing objects by + index, however there is no guarantee as to their order. + + All Sets are observable via the Enumerable Observer API - which works + on any enumerable object including both Sets and Arrays. + + ## Creating a Set + + You can create a set like you would most objects using + `new Ember.Set()`. Most new sets you create will be empty, but you can + also initialize the set with some content by passing an array or other + enumerable of objects to the constructor. + + Finally, you can pass in an existing set and the set will be copied. You + can also create a copy of a set by calling `Ember.Set#copy()`. + + ```javascript + // creates a new empty set + var foundNames = new Ember.Set(); + + // creates a set with four names in it. + var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P + + // creates a copy of the names set. + var namesCopy = new Ember.Set(names); + + // same as above. + var anotherNamesCopy = names.copy(); + ``` + + ## Adding/Removing Objects + + You generally add or remove objects from a set using `add()` or + `remove()`. You can add any type of object including primitives such as + numbers, strings, and booleans. + + Unlike arrays, objects can only exist one time in a set. If you call `add()` + on a set with the same object multiple times, the object will only be added + once. Likewise, calling `remove()` with the same object multiple times will + remove the object the first time and have no effect on future calls until + you add the object to the set again. + + NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do + so will be ignored. + + In addition to add/remove you can also call `push()`/`pop()`. Push behaves + just like `add()` but `pop()`, unlike `remove()` will pick an arbitrary + object, remove it and return it. This is a good way to use a set as a job + queue when you don't care which order the jobs are executed in. + + ## Testing for an Object + + To test for an object's presence in a set you simply call + `Ember.Set#contains()`. + + ## Observing changes + + When using `Ember.Set`, you can observe the `"[]"` property to be + alerted whenever the content changes. You can also add an enumerable + observer to the set to be notified of specific objects that are added and + removed from the set. See `Ember.Enumerable` for more information on + enumerables. + + This is often unhelpful. If you are filtering sets of objects, for instance, + it is very inefficient to re-filter all of the items each time the set + changes. It would be better if you could just adjust the filtered set based + on what was changed on the original set. The same issue applies to merging + sets, as well. + + ## Other Methods + + `Ember.Set` primary implements other mixin APIs. For a complete reference + on the methods you will use with `Ember.Set`, please consult these mixins. + The most useful ones will be `Ember.Enumerable` and + `Ember.MutableEnumerable` which implement most of the common iterator + methods you are used to on Array. + + Note that you can also use the `Ember.Copyable` and `Ember.Freezable` + APIs on `Ember.Set` as well. Once a set is frozen it can no longer be + modified. The benefit of this is that when you call `frozenCopy()` on it, + Ember will avoid making copies of the set. This allows you to write + code that can know with certainty when the underlying set data will or + will not be modified. + + @class Set + @namespace Ember + @extends Ember.CoreObject + @uses Ember.MutableEnumerable + @uses Ember.Copyable + @uses Ember.Freezable + @since Ember 0.9 +*/ +Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Ember.Freezable, + /** @scope Ember.Set.prototype */ { + + // .......................................................... + // IMPLEMENT ENUMERABLE APIS + // + + /** + This property will change as the number of objects in the set changes. + + @property length + @type number + @default 0 + */ + length: 0, + + /** + Clears the set. This is useful if you want to reuse an existing set + without having to recreate it. + + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.length; // 3 + colors.clear(); + colors.length; // 0 + ``` + + @method clear + @return {Ember.Set} An empty Set + */ + clear: function() { + if (this.isFrozen) { throw new Error(Ember.FROZEN_ERROR); } + + var len = get(this, 'length'); + if (len === 0) { return this; } + + var guid; + + this.enumerableContentWillChange(len, 0); + Ember.propertyWillChange(this, 'firstObject'); + Ember.propertyWillChange(this, 'lastObject'); + + for (var i=0; i < len; i++){ + guid = guidFor(this[i]); + delete this[guid]; + delete this[i]; + } + + set(this, 'length', 0); + + Ember.propertyDidChange(this, 'firstObject'); + Ember.propertyDidChange(this, 'lastObject'); + this.enumerableContentDidChange(len, 0); + + return this; + }, + + /** + Returns true if the passed object is also an enumerable that contains the + same objects as the receiver. + + ```javascript + var colors = ["red", "green", "blue"], + same_colors = new Ember.Set(colors); + + same_colors.isEqual(colors); // true + same_colors.isEqual(["purple", "brown"]); // false + ``` + + @method isEqual + @param {Ember.Set} obj the other object. + @return {Boolean} + */ + isEqual: function(obj) { + // fail fast + if (!Ember.Enumerable.detect(obj)) return false; + + var loc = get(this, 'length'); + if (get(obj, 'length') !== loc) return false; + + while(--loc >= 0) { + if (!obj.contains(this[loc])) return false; + } + + return true; + }, + + /** + Adds an object to the set. Only non-`null` objects can be added to a set + and those can only be added once. If the object is already in the set or + the passed value is null this method will have no effect. + + This is an alias for `Ember.MutableEnumerable.addObject()`. + + ```javascript + var colors = new Ember.Set(); + colors.add("blue"); // ["blue"] + colors.add("blue"); // ["blue"] + colors.add("red"); // ["blue", "red"] + colors.add(null); // ["blue", "red"] + colors.add(undefined); // ["blue", "red"] + ``` + + @method add + @param {Object} obj The object to add. + @return {Ember.Set} The set itself. + */ + add: Ember.aliasMethod('addObject'), + + /** + Removes the object from the set if it is found. If you pass a `null` value + or an object that is already not in the set, this method will have no + effect. This is an alias for `Ember.MutableEnumerable.removeObject()`. + + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.remove("red"); // ["blue", "green"] + colors.remove("purple"); // ["blue", "green"] + colors.remove(null); // ["blue", "green"] + ``` + + @method remove + @param {Object} obj The object to remove + @return {Ember.Set} The set itself. + */ + remove: Ember.aliasMethod('removeObject'), + + /** + Removes the last element from the set and returns it, or `null` if it's empty. + + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.pop(); // "blue" + colors.pop(); // "green" + colors.pop(); // null + ``` + + @method pop + @return {Object} The removed object from the set or null. + */ + pop: function() { + if (get(this, 'isFrozen')) throw new Error(Ember.FROZEN_ERROR); + var obj = this.length > 0 ? this[this.length-1] : null; + this.remove(obj); + return obj; + }, + + /** + Inserts the given object on to the end of the set. It returns + the set itself. + + This is an alias for `Ember.MutableEnumerable.addObject()`. + + ```javascript + var colors = new Ember.Set(); + colors.push("red"); // ["red"] + colors.push("green"); // ["red", "green"] + colors.push("blue"); // ["red", "green", "blue"] + ``` + + @method push + @return {Ember.Set} The set itself. + */ + push: Ember.aliasMethod('addObject'), + + /** + Removes the last element from the set and returns it, or `null` if it's empty. + + This is an alias for `Ember.Set.pop()`. + + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.shift(); // "blue" + colors.shift(); // "green" + colors.shift(); // null + ``` + + @method shift + @return {Object} The removed object from the set or null. + */ + shift: Ember.aliasMethod('pop'), + + /** + Inserts the given object on to the end of the set. It returns + the set itself. + + This is an alias of `Ember.Set.push()` + + ```javascript + var colors = new Ember.Set(); + colors.unshift("red"); // ["red"] + colors.unshift("green"); // ["red", "green"] + colors.unshift("blue"); // ["red", "green", "blue"] + ``` + + @method unshift + @return {Ember.Set} The set itself. + */ + unshift: Ember.aliasMethod('push'), + + /** + Adds each object in the passed enumerable to the set. + + This is an alias of `Ember.MutableEnumerable.addObjects()` + + ```javascript + var colors = new Ember.Set(); + colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + ``` + + @method addEach + @param {Ember.Enumerable} objects the objects to add. + @return {Ember.Set} The set itself. + */ + addEach: Ember.aliasMethod('addObjects'), + + /** + Removes each object in the passed enumerable to the set. + + This is an alias of `Ember.MutableEnumerable.removeObjects()` + + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.removeEach(["red", "blue"]); // ["green"] + ``` + + @method removeEach + @param {Ember.Enumerable} objects the objects to remove. + @return {Ember.Set} The set itself. + */ + removeEach: Ember.aliasMethod('removeObjects'), + + // .......................................................... + // PRIVATE ENUMERABLE SUPPORT + // + + init: function(items) { + this._super(); + if (items) this.addObjects(items); + }, + + // implement Ember.Enumerable + nextObject: function(idx) { + return this[idx]; + }, + + // more optimized version + firstObject: Ember.computed(function() { + return this.length > 0 ? this[0] : undefined; + }), + + // more optimized version + lastObject: Ember.computed(function() { + return this.length > 0 ? this[this.length-1] : undefined; + }), + + // implements Ember.MutableEnumerable + addObject: function(obj) { + if (get(this, 'isFrozen')) throw new Error(Ember.FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do + + var guid = guidFor(obj), + idx = this[guid], + len = get(this, 'length'), + added ; + + if (idx>=0 && idx<len && (this[idx] === obj)) return this; // added + + added = [obj]; + + this.enumerableContentWillChange(null, added); + Ember.propertyWillChange(this, 'lastObject'); + + len = get(this, 'length'); + this[guid] = len; + this[len] = obj; + set(this, 'length', len+1); + + Ember.propertyDidChange(this, 'lastObject'); + this.enumerableContentDidChange(null, added); + + return this; + }, + + // implements Ember.MutableEnumerable + removeObject: function(obj) { + if (get(this, 'isFrozen')) throw new Error(Ember.FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do + + var guid = guidFor(obj), + idx = this[guid], + len = get(this, 'length'), + isFirst = idx === 0, + isLast = idx === len-1, + last, removed; + + + if (idx>=0 && idx<len && (this[idx] === obj)) { + removed = [obj]; + + this.enumerableContentWillChange(removed, null); + if (isFirst) { Ember.propertyWillChange(this, 'firstObject'); } + if (isLast) { Ember.propertyWillChange(this, 'lastObject'); } + + // swap items - basically move the item to the end so it can be removed + if (idx < len-1) { + last = this[len-1]; + this[idx] = last; + this[guidFor(last)] = idx; + } + + delete this[guid]; + delete this[len-1]; + set(this, 'length', len-1); + + if (isFirst) { Ember.propertyDidChange(this, 'firstObject'); } + if (isLast) { Ember.propertyDidChange(this, 'lastObject'); } + this.enumerableContentDidChange(removed, null); + } + + return this; + }, + + // optimized version + contains: function(obj) { + return this[guidFor(obj)]>=0; + }, + + copy: function() { + var C = this.constructor, ret = new C(), loc = get(this, 'length'); + set(ret, 'length', loc); + while(--loc>=0) { + ret[loc] = this[loc]; + ret[guidFor(this[loc])] = loc; + } + return ret; + }, + + toString: function() { + var len = this.length, idx, array = []; + for(idx = 0; idx < len; idx++) { + array[idx] = this[idx]; + } + return fmt("Ember.Set<%@>", [array.join(',')]); + } + +}); + +})(); + + + +(function() { +var DeferredMixin = Ember.DeferredMixin, // mixins/deferred + get = Ember.get; + +var Deferred = Ember.Object.extend(DeferredMixin); + +Deferred.reopenClass({ + promise: function(callback, binding) { + var deferred = Deferred.create(); + callback.call(binding, deferred); + return deferred; + } +}); + +Ember.Deferred = Deferred; + +})(); + + + +(function() { +var forEach = Ember.ArrayPolyfills.forEach; + +/** +@module ember +@submodule ember-runtime +*/ + +var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; +var loaded = {}; + +/** +@method onLoad +@for Ember +@param name {String} name of hook +@param callback {Function} callback to be called +*/ +Ember.onLoad = function(name, callback) { + var object; + + loadHooks[name] = loadHooks[name] || Ember.A(); + loadHooks[name].pushObject(callback); + + if (object = loaded[name]) { + callback(object); + } +}; + +/** +@method runLoadHooks +@for Ember +@param name {String} name of hook +@param object {Object} object to pass to callbacks +*/ +Ember.runLoadHooks = function(name, object) { + loaded[name] = object; + + if (loadHooks[name]) { + forEach.call(loadHooks[name], function(callback) { + callback(object); + }); + } +}; + +})(); + + + +(function() { + +})(); + + + +(function() { +var get = Ember.get; + +/** +@module ember +@submodule ember-runtime +*/ + +/** + `Ember.ControllerMixin` provides a standard interface for all classes that + compose Ember's controller layer: `Ember.Controller`, + `Ember.ArrayController`, and `Ember.ObjectController`. + + Within an `Ember.Router`-managed application single shared instaces of every + Controller object in your application's namespace will be added to the + application's `Ember.Router` instance. See `Ember.Application#initialize` + for additional information. + + ## Views + + By default a controller instance will be the rendering context + for its associated `Ember.View.` This connection is made during calls to + `Ember.ControllerMixin#connectOutlet`. + + Within the view's template, the `Ember.View` instance can be accessed + through the controller with `{{view}}`. + + ## Target Forwarding + + By default a controller will target your application's `Ember.Router` + instance. Calls to `{{action}}` within the template of a controller's view + are forwarded to the router. See `Ember.Handlebars.helpers.action` for + additional information. + + @class ControllerMixin + @namespace Ember + @extends Ember.Mixin +*/ +Ember.ControllerMixin = Ember.Mixin.create({ + /* ducktype as a controller */ + isController: true, + + /** + The object to which events from the view should be sent. + + For example, when a Handlebars template uses the `{{action}}` helper, + it will attempt to send the event to the view's controller's `target`. + + By default, a controller's `target` is set to the router after it is + instantiated by `Ember.Application#initialize`. + + @property target + @default null + */ + target: null, + + container: null, + + parentController: null, + + store: null, + + model: Ember.computed.alias('content'), + + send: function(actionName) { + var args = [].slice.call(arguments, 1), target; + + if (this[actionName]) { + Ember.assert("The controller " + this + " does not have the action " + actionName, typeof this[actionName] === 'function'); + this[actionName].apply(this, args); + } else if(target = get(this, 'target')) { + Ember.assert("The target for controller " + this + " (" + target + ") did not define a `send` method", typeof target.send === 'function'); + target.send.apply(target, arguments); + } + } +}); + +/** + @class Controller + @namespace Ember + @extends Ember.Object + @uses Ember.ControllerMixin +*/ +Ember.Controller = Ember.Object.extend(Ember.ControllerMixin); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach; + +/** + `Ember.SortableMixin` provides a standard interface for array proxies + to specify a sort order and maintain this sorting when objects are added, + removed, or updated without changing the implicit order of their underlying + content array: + + ```javascript + songs = [ + {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'}, + {trackNumber: 2, title: 'Back in the U.S.S.R.'}, + {trackNumber: 3, title: 'Glass Onion'}, + ]; + + songsController = Ember.ArrayController.create({ + content: songs, + sortProperties: ['trackNumber'], + sortAscending: true + }); + + songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} + + songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); + songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} + ``` + + @class SortableMixin + @namespace Ember + @extends Ember.Mixin + @uses Ember.MutableEnumerable +*/ +Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, { + + /** + Specifies which properties dictate the arrangedContent's sort order. + + @property {Array} sortProperties + */ + sortProperties: null, + + /** + Specifies the arrangedContent's sort direction + + @property {Boolean} sortAscending + */ + sortAscending: true, + + /** + The function used to compare two values. You can override this if you + want to do custom comparisons.Functions must be of the type expected by + Array#sort, i.e. + return 0 if the two parameters are equal, + return a negative value if the first parameter is smaller than the second or + return a positive value otherwise: + + ```javascript + function(x,y){ // These are assumed to be integers + if(x === y) + return 0; + return x < y ? -1 : 1; + } + ``` + + @property sortFunction + @type {Function} + @default Ember.compare + */ + sortFunction: Ember.compare, + + orderBy: function(item1, item2) { + var result = 0, + sortProperties = get(this, 'sortProperties'), + sortAscending = get(this, 'sortAscending'), + sortFunction = get(this, 'sortFunction'); + + Ember.assert("you need to define `sortProperties`", !!sortProperties); + + forEach(sortProperties, function(propertyName) { + if (result === 0) { + result = sortFunction(get(item1, propertyName), get(item2, propertyName)); + if ((result !== 0) && !sortAscending) { + result = (-1) * result; + } + } + }); + + return result; + }, + + destroy: function() { + var content = get(this, 'content'), + sortProperties = get(this, 'sortProperties'); + + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } + + return this._super(); + }, + + isSorted: Ember.computed.bool('sortProperties'), + + arrangedContent: Ember.computed('content', 'sortProperties.@each', function(key, value) { + var content = get(this, 'content'), + isSorted = get(this, 'isSorted'), + sortProperties = get(this, 'sortProperties'), + self = this; + + if (content && isSorted) { + content = content.slice(); + content.sort(function(item1, item2) { + return self.orderBy(item1, item2); + }); + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + return Ember.A(content); + } + + return content; + }), + + _contentWillChange: Ember.beforeObserver(function() { + var content = get(this, 'content'), + sortProperties = get(this, 'sortProperties'); + + if (content && sortProperties) { + forEach(content, function(item) { + forEach(sortProperties, function(sortProperty) { + Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } + + this._super(); + }, 'content'), + + sortAscendingWillChange: Ember.beforeObserver(function() { + this._lastSortAscending = get(this, 'sortAscending'); + }, 'sortAscending'), + + sortAscendingDidChange: Ember.observer(function() { + if (get(this, 'sortAscending') !== this._lastSortAscending) { + var arrangedContent = get(this, 'arrangedContent'); + arrangedContent.reverseObjects(); + } + }, 'sortAscending'), + + contentArrayWillChange: function(array, idx, removedCount, addedCount) { + var isSorted = get(this, 'isSorted'); + + if (isSorted) { + var arrangedContent = get(this, 'arrangedContent'); + var removedObjects = array.slice(idx, idx+removedCount); + var sortProperties = get(this, 'sortProperties'); + + forEach(removedObjects, function(item) { + arrangedContent.removeObject(item); + + forEach(sortProperties, function(sortProperty) { + Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } + + return this._super(array, idx, removedCount, addedCount); + }, + + contentArrayDidChange: function(array, idx, removedCount, addedCount) { + var isSorted = get(this, 'isSorted'), + sortProperties = get(this, 'sortProperties'); + + if (isSorted) { + var addedObjects = array.slice(idx, idx+addedCount); + + forEach(addedObjects, function(item) { + this.insertItemSorted(item); + + forEach(sortProperties, function(sortProperty) { + Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); + }, this); + }, this); + } + + return this._super(array, idx, removedCount, addedCount); + }, + + insertItemSorted: function(item) { + var arrangedContent = get(this, 'arrangedContent'); + var length = get(arrangedContent, 'length'); + + var idx = this._binarySearch(item, 0, length); + arrangedContent.insertAt(idx, item); + }, + + contentItemSortPropertyDidChange: function(item) { + var arrangedContent = get(this, 'arrangedContent'), + oldIndex = arrangedContent.indexOf(item), + leftItem = arrangedContent.objectAt(oldIndex - 1), + rightItem = arrangedContent.objectAt(oldIndex + 1), + leftResult = leftItem && this.orderBy(item, leftItem), + rightResult = rightItem && this.orderBy(item, rightItem); + + if (leftResult < 0 || rightResult > 0) { + arrangedContent.removeObject(item); + this.insertItemSorted(item); + } + }, + + _binarySearch: function(item, low, high) { + var mid, midItem, res, arrangedContent; + + if (low === high) { + return low; + } + + arrangedContent = get(this, 'arrangedContent'); + + mid = low + Math.floor((high - low) / 2); + midItem = arrangedContent.objectAt(mid); + + res = this.orderBy(midItem, item); + + if (res < 0) { + return this._binarySearch(item, mid+1, high); + } else if (res > 0) { + return this._binarySearch(item, low, mid); + } + + return mid; + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach, + replace = Ember.EnumerableUtils.replace; + +/** + `Ember.ArrayController` provides a way for you to publish a collection of + objects so that you can easily bind to the collection from a Handlebars + `#each` helper, an `Ember.CollectionView`, or other controllers. + + The advantage of using an `ArrayController` is that you only have to set up + your view bindings once; to change what's displayed, simply swap out the + `content` property on the controller. + + For example, imagine you wanted to display a list of items fetched via an XHR + request. Create an `Ember.ArrayController` and set its `content` property: + + ```javascript + MyApp.listController = Ember.ArrayController.create(); + + $.get('people.json', function(data) { + MyApp.listController.set('content', data); + }); + ``` + + Then, create a view that binds to your new controller: + + ```handlebars + {{#each MyApp.listController}} + {{firstName}} {{lastName}} + {{/each}} + ``` + + Although you are binding to the controller, the behavior of this controller + is to pass through any methods or properties to the underlying array. This + capability comes from `Ember.ArrayProxy`, which this class inherits from. + + Sometimes you want to display computed properties within the body of an + `#each` helper that depend on the underlying items in `content`, but are not + present on those items. To do this, set `itemController` to the name of a + controller (probably an `ObjectController`) that will wrap each individual item. + + For example: + + ```handlebars + {{#each post in controller}} + <li>{{title}} ({{titleLength}} characters)</li> + {{/each}} + ``` + + ```javascript + App.PostsController = Ember.ArrayController.extend({ + itemController: 'post' + }); + + App.PostController = Ember.ObjectController.extend({ + // the `title` property will be proxied to the underlying post. + + titleLength: function() { + return this.get('title').length; + }.property('title') + }); + ``` + + In some cases it is helpful to return a different `itemController` depending + on the particular item. Subclasses can do this by overriding + `lookupItemController`. + + For example: + + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController + } + } + }); + ``` + + The itemController instances will have a `parentController` property set to + either the the `parentController` property of the `ArrayController` + or to the `ArrayController` instance itself. + + @class ArrayController + @namespace Ember + @extends Ember.ArrayProxy + @uses Ember.SortableMixin + @uses Ember.ControllerMixin +*/ + +Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin, + Ember.SortableMixin, { + + /** + The controller used to wrap items, if any. + + @property itemController + @type String + @default null + */ + itemController: null, + + /** + Return the name of the controller to wrap items, or `null` if items should + be returned directly. The default implementation simply returns the + `itemController` property, but subclasses can override this method to return + different controllers for different objects. + + For example: + + ```javascript + App.MyArrayController = Ember.ArrayController.extend({ + lookupItemController: function( object ) { + if (object.get('isSpecial')) { + return "special"; // use App.SpecialController + } else { + return "regular"; // use App.RegularController + } + } + }); + ``` + + @method lookupItemController + @param {Object} object + @return {String} + */ + lookupItemController: function(object) { + return get(this, 'itemController'); + }, + + objectAtContent: function(idx) { + var length = get(this, 'length'), + arrangedContent = get(this,'arrangedContent'), + object = arrangedContent && arrangedContent.objectAt(idx); + + if (idx >= 0 && idx < length) { + var controllerClass = this.lookupItemController(object); + if (controllerClass) { + return this.controllerAt(idx, object, controllerClass); + } + } + + // When `controllerClass` is falsy, we have not opted in to using item + // controllers, so return the object directly. + + // When the index is out of range, we want to return the "out of range" + // value, whatever that might be. Rather than make assumptions + // (e.g. guessing `null` or `undefined`) we defer this to `arrangedContent`. + return object; + }, + + arrangedContentDidChange: function() { + this._super(); + this._resetSubControllers(); + }, + + arrayContentDidChange: function(idx, removedCnt, addedCnt) { + var subControllers = get(this, '_subControllers'), + subControllersToRemove = subControllers.slice(idx, idx+removedCnt); + + forEach(subControllersToRemove, function(subController) { + if (subController) { subController.destroy(); } + }); + + replace(subControllers, idx, removedCnt, new Array(addedCnt)); + + // The shadow array of subcontrollers must be updated before we trigger + // observers, otherwise observers will get the wrong subcontainer when + // calling `objectAt` + this._super(idx, removedCnt, addedCnt); + }, + + init: function() { + if (!this.get('content')) { Ember.defineProperty(this, 'content', undefined, Ember.A()); } + this._super(); + this.set('_subControllers', Ember.A()); + }, + + controllerAt: function(idx, object, controllerClass) { + var container = get(this, 'container'), + subControllers = get(this, '_subControllers'), + subController = subControllers[idx]; + + if (!subController) { + subController = container.lookup("controller:" + controllerClass, { singleton: false }); + subControllers[idx] = subController; + } + + if (!subController) { + throw new Error('Could not resolve itemController: "' + controllerClass + '"'); + } + + subController.set('target', this); + subController.set('parentController', get(this, 'parentController') || this); + subController.set('content', object); + + return subController; + }, + + _subControllers: null, + + _resetSubControllers: function() { + var subControllers = get(this, '_subControllers'); + if (subControllers) { + forEach(subControllers, function(subController) { + if (subController) { subController.destroy(); } + }); + } + + this.set('_subControllers', Ember.A()); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-runtime +*/ + +/** + `Ember.ObjectController` is part of Ember's Controller layer. A single shared + instance of each `Ember.ObjectController` subclass in your application's + namespace will be created at application initialization and be stored on your + application's `Ember.Router` instance. + + `Ember.ObjectController` derives its functionality from its superclass + `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin. + + @class ObjectController + @namespace Ember + @extends Ember.ObjectProxy + @uses Ember.ControllerMixin +**/ +Ember.ObjectController = Ember.ObjectProxy.extend(Ember.ControllerMixin); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +Ember Runtime + +@module ember +@submodule ember-runtime +@requires ember-metal +*/ + +})(); + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var jQuery = Ember.imports.jQuery; +Ember.assert("Ember Views require jQuery 1.7, 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(7|8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY)); + +/** + Alias for jQuery + + @method $ + @for Ember +*/ +Ember.$ = jQuery; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ +if (Ember.$) { + // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents + var dragEvents = Ember.String.w('dragstart drag dragenter dragleave dragover drop dragend'); + + // Copies the `dataTransfer` property from a browser event object onto the + // jQuery event object for the specified events + Ember.EnumerableUtils.forEach(dragEvents, function(eventName) { + Ember.$.event.fixHooks[eventName] = { props: ['dataTransfer'] }; + }); +} + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +/* BEGIN METAMORPH HELPERS */ + +// Internet Explorer prior to 9 does not allow setting innerHTML if the first element +// is a "zero-scope" element. This problem can be worked around by making +// the first node an invisible text node. We, like Modernizr, use ­ + +var needsShy = this.document && (function(){ + var testEl = document.createElement('div'); + testEl.innerHTML = "<div></div>"; + testEl.firstChild.innerHTML = "<script></script>"; + return testEl.firstChild.innerHTML === ''; +})(); + +// IE 8 (and likely earlier) likes to move whitespace preceeding +// a script tag to appear after it. This means that we can +// accidentally remove whitespace when updating a morph. +var movesWhitespace = this.document && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "Test: <script type='text/x-placeholder'></script>Value"; + return testEl.childNodes[0].nodeValue === 'Test:' && + testEl.childNodes[2].nodeValue === ' Value'; +})(); + +// Use this to find children by ID instead of using jQuery +var findChildById = function(element, id) { + if (element.getAttribute('id') === id) { return element; } + + var len = element.childNodes.length, idx, node, found; + for (idx=0; idx<len; idx++) { + node = element.childNodes[idx]; + found = node.nodeType === 1 && findChildById(node, id); + if (found) { return found; } + } +}; + +var setInnerHTMLWithoutFix = function(element, html) { + if (needsShy) { + html = '­' + html; + } + + var matches = []; + if (movesWhitespace) { + // Right now we only check for script tags with ids with the + // goal of targeting morphs. + html = html.replace(/(\s+)(<script id='([^']+)')/g, function(match, spaces, tag, id) { + matches.push([id, spaces]); + return tag; + }); + } + + element.innerHTML = html; + + // If we have to do any whitespace adjustments do them now + if (matches.length > 0) { + var len = matches.length, idx; + for (idx=0; idx<len; idx++) { + var script = findChildById(element, matches[idx][0]), + node = document.createTextNode(matches[idx][1]); + script.parentNode.insertBefore(node, script); + } + } + + if (needsShy) { + var shyElement = element.firstChild; + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } + } +}; + +/* END METAMORPH HELPERS */ + + +var innerHTMLTags = {}; +var canSetInnerHTML = function(tagName) { + if (innerHTMLTags[tagName] !== undefined) { + return innerHTMLTags[tagName]; + } + + var canSet = true; + + // IE 8 and earlier don't allow us to do innerHTML on select + if (tagName.toLowerCase() === 'select') { + var el = document.createElement('select'); + setInnerHTMLWithoutFix(el, '<option value="test">Test</option>'); + canSet = el.options.length === 1; + } + + innerHTMLTags[tagName] = canSet; + + return canSet; +}; + +var setInnerHTML = function(element, html) { + var tagName = element.tagName; + + if (canSetInnerHTML(tagName)) { + setInnerHTMLWithoutFix(element, html); + } else { + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); + Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); + + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], + endTag = '</'+tagName+'>'; + + var wrapper = document.createElement('div'); + setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); + element = wrapper.firstChild; + while (element.tagName !== tagName) { + element = element.nextSibling; + } + } + + return element; +}; + +function isSimpleClick(event) { + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, + secondaryClick = event.which > 1; // IE9 may return undefined + + return !modifier && !secondaryClick; +} + +Ember.ViewUtils = { + setInnerHTML: setInnerHTML, + isSimpleClick: isSimpleClick +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; + +var ClassSet = function() { + this.seen = {}; + this.list = []; +}; + +ClassSet.prototype = { + add: function(string) { + if (string in this.seen) { return; } + this.seen[string] = true; + + this.list.push(string); + }, + + toDOM: function() { + return this.list.join(" "); + } +}; + +/** + `Ember.RenderBuffer` gathers information regarding the a view and generates the + final representation. `Ember.RenderBuffer` will generate HTML which can be pushed + to the DOM. + + @class RenderBuffer + @namespace Ember + @constructor +*/ +Ember.RenderBuffer = function(tagName) { + return new Ember._RenderBuffer(tagName); +}; + +Ember._RenderBuffer = function(tagName) { + this.tagNames = [tagName || null]; + this.buffer = ""; +}; + +Ember._RenderBuffer.prototype = +/** @scope Ember.RenderBuffer.prototype */ { + + // The root view's element + _element: null, + + _hasElement: true, + + /** + @private + + An internal set used to de-dupe class names when `addClass()` is + used. After each call to `addClass()`, the `classes` property + will be updated. + + @property elementClasses + @type Array + @default [] + */ + elementClasses: null, + + /** + Array of class names which will be applied in the class attribute. + + You can use `setClasses()` to set this property directly. If you + use `addClass()`, it will be maintained for you. + + @property classes + @type Array + @default [] + */ + classes: null, + + /** + The id in of the element, to be applied in the id attribute. + + You should not set this property yourself, rather, you should use + the `id()` method of `Ember.RenderBuffer`. + + @property elementId + @type String + @default null + */ + elementId: null, + + /** + A hash keyed on the name of the attribute and whose value will be + applied to that attribute. For example, if you wanted to apply a + `data-view="Foo.bar"` property to an element, you would set the + elementAttributes hash to `{'data-view':'Foo.bar'}`. + + You should not maintain this hash yourself, rather, you should use + the `attr()` method of `Ember.RenderBuffer`. + + @property elementAttributes + @type Hash + @default {} + */ + elementAttributes: null, + + /** + A hash keyed on the name of the properties and whose value will be + applied to that property. For example, if you wanted to apply a + `checked=true` property to an element, you would set the + elementProperties hash to `{'checked':true}`. + + You should not maintain this hash yourself, rather, you should use + the `prop()` method of `Ember.RenderBuffer`. + + @property elementProperties + @type Hash + @default {} + */ + elementProperties: null, + + /** + The tagname of the element an instance of `Ember.RenderBuffer` represents. + + Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For + example, if you wanted to create a `p` tag, then you would call + + ```javascript + Ember.RenderBuffer('p') + ``` + + @property elementTag + @type String + @default null + */ + elementTag: null, + + /** + A hash keyed on the name of the style attribute and whose value will + be applied to that attribute. For example, if you wanted to apply a + `background-color:black;` style to an element, you would set the + elementStyle hash to `{'background-color':'black'}`. + + You should not maintain this hash yourself, rather, you should use + the `style()` method of `Ember.RenderBuffer`. + + @property elementStyle + @type Hash + @default {} + */ + elementStyle: null, + + /** + Nested `RenderBuffers` will set this to their parent `RenderBuffer` + instance. + + @property parentBuffer + @type Ember._RenderBuffer + */ + parentBuffer: null, + + /** + Adds a string of HTML to the `RenderBuffer`. + + @method push + @param {String} string HTML to push into the buffer + @chainable + */ + push: function(string) { + this.buffer += string; + return this; + }, + + /** + Adds a class to the buffer, which will be rendered to the class attribute. + + @method addClass + @param {String} className Class name to add to the buffer + @chainable + */ + addClass: function(className) { + // lazily create elementClasses + this.elementClasses = (this.elementClasses || new ClassSet()); + this.elementClasses.add(className); + this.classes = this.elementClasses.list; + + return this; + }, + + setClasses: function(classNames) { + this.classes = classNames; + }, + + /** + Sets the elementID to be used for the element. + + @method id + @param {String} id + @chainable + */ + id: function(id) { + this.elementId = id; + return this; + }, + + // duck type attribute functionality like jQuery so a render buffer + // can be used like a jQuery object in attribute binding scenarios. + + /** + Adds an attribute which will be rendered to the element. + + @method attr + @param {String} name The name of the attribute + @param {String} value The value to add to the attribute + @chainable + @return {Ember.RenderBuffer|String} this or the current attribute value + */ + attr: function(name, value) { + var attributes = this.elementAttributes = (this.elementAttributes || {}); + + if (arguments.length === 1) { + return attributes[name]; + } else { + attributes[name] = value; + } + + return this; + }, + + /** + Remove an attribute from the list of attributes to render. + + @method removeAttr + @param {String} name The name of the attribute + @chainable + */ + removeAttr: function(name) { + var attributes = this.elementAttributes; + if (attributes) { delete attributes[name]; } + + return this; + }, + + /** + Adds an property which will be rendered to the element. + + @method prop + @param {String} name The name of the property + @param {String} value The value to add to the property + @chainable + @return {Ember.RenderBuffer|String} this or the current property value + */ + prop: function(name, value) { + var properties = this.elementProperties = (this.elementProperties || {}); + + if (arguments.length === 1) { + return properties[name]; + } else { + properties[name] = value; + } + + return this; + }, + + /** + Remove an property from the list of properties to render. + + @method removeProp + @param {String} name The name of the property + @chainable + */ + removeProp: function(name) { + var properties = this.elementProperties; + if (properties) { delete properties[name]; } + + return this; + }, + + /** + Adds a style to the style attribute which will be rendered to the element. + + @method style + @param {String} name Name of the style + @param {String} value + @chainable + */ + style: function(name, value) { + this.elementStyle = (this.elementStyle || {}); + + this.elementStyle[name] = value; + return this; + }, + + begin: function(tagName) { + this.tagNames.push(tagName || null); + return this; + }, + + pushOpeningTag: function() { + var tagName = this.currentTagName(); + if (!tagName) { return; } + + if (this._hasElement && !this._element && this.buffer.length === 0) { + this._element = this.generateElement(); + return; + } + + var buffer = this.buffer, + id = this.elementId, + classes = this.classes, + attrs = this.elementAttributes, + props = this.elementProperties, + style = this.elementStyle, + attr, prop; + + buffer += '<' + tagName; + + if (id) { + buffer += ' id="' + this._escapeAttribute(id) + '"'; + this.elementId = null; + } + if (classes) { + buffer += ' class="' + this._escapeAttribute(classes.join(' ')) + '"'; + this.classes = null; + } + + if (style) { + buffer += ' style="'; + + for (prop in style) { + if (style.hasOwnProperty(prop)) { + buffer += prop + ':' + this._escapeAttribute(style[prop]) + ';'; + } + } + + buffer += '"'; + + this.elementStyle = null; + } + + if (attrs) { + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + buffer += ' ' + attr + '="' + this._escapeAttribute(attrs[attr]) + '"'; + } + } + + this.elementAttributes = null; + } + + if (props) { + for (prop in props) { + if (props.hasOwnProperty(prop)) { + var value = props[prop]; + if (value || typeof(value) === 'number') { + if (value === true) { + buffer += ' ' + prop + '="' + prop + '"'; + } else { + buffer += ' ' + prop + '="' + this._escapeAttribute(props[prop]) + '"'; + } + } + } + } + + this.elementProperties = null; + } + + buffer += '>'; + this.buffer = buffer; + }, + + pushClosingTag: function() { + var tagName = this.tagNames.pop(); + if (tagName) { this.buffer += '</' + tagName + '>'; } + }, + + currentTagName: function() { + return this.tagNames[this.tagNames.length-1]; + }, + + generateElement: function() { + var tagName = this.tagNames.pop(), // pop since we don't need to close + element = document.createElement(tagName), + $element = Ember.$(element), + id = this.elementId, + classes = this.classes, + attrs = this.elementAttributes, + props = this.elementProperties, + style = this.elementStyle, + styleBuffer = '', attr, prop; + + if (id) { + $element.attr('id', id); + this.elementId = null; + } + if (classes) { + $element.attr('class', classes.join(' ')); + this.classes = null; + } + + if (style) { + for (prop in style) { + if (style.hasOwnProperty(prop)) { + styleBuffer += (prop + ':' + style[prop] + ';'); + } + } + + $element.attr('style', styleBuffer); + + this.elementStyle = null; + } + + if (attrs) { + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + $element.attr(attr, attrs[attr]); + } + } + + this.elementAttributes = null; + } + + if (props) { + for (prop in props) { + if (props.hasOwnProperty(prop)) { + $element.prop(prop, props[prop]); + } + } + + this.elementProperties = null; + } + + return element; + }, + + /** + @method element + @return {DOMElement} The element corresponding to the generated HTML + of this buffer + */ + element: function() { + var html = this.innerString(); + + if (html) { + this._element = Ember.ViewUtils.setInnerHTML(this._element, html); + } + + return this._element; + }, + + /** + Generates the HTML content for this buffer. + + @method string + @return {String} The generated HTML + */ + string: function() { + if (this._hasElement && this._element) { + // Firefox versions < 11 do not have support for element.outerHTML. + var thisElement = this.element(), outerHTML = thisElement.outerHTML; + if (typeof outerHTML === 'undefined'){ + return Ember.$('<div/>').append(thisElement).html(); + } + return outerHTML; + } else { + return this.innerString(); + } + }, + + innerString: function() { + return this.buffer; + }, + + _escapeAttribute: function(value) { + // Stolen shamelessly from Handlebars + + var escape = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /&(?!\w+;)|[<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function(chr) { + return escape[chr] || "&"; + }; + + var string = value.toString(); + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + } + +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; + +/** + `Ember.EventDispatcher` handles delegating browser events to their + corresponding `Ember.Views.` For example, when you click on a view, + `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets + called. + + @class EventDispatcher + @namespace Ember + @private + @extends Ember.Object +*/ +Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{ + + /** + The set of events names (and associated handler function names) to be setup + and dispatched by the `EventDispatcher`. Custom events can added to this list at setup + time, generally via the `Ember.Application.customEvents` hash. Only override this + default set to prevent the EventDispatcher from listening on some events all together. + + This set will be modified by `setup` to also include any events added at that time. + + @property events + @type Object + */ + events: { + touchstart : 'touchStart', + touchmove : 'touchMove', + touchend : 'touchEnd', + touchcancel : 'touchCancel', + keydown : 'keyDown', + keyup : 'keyUp', + keypress : 'keyPress', + mousedown : 'mouseDown', + mouseup : 'mouseUp', + contextmenu : 'contextMenu', + click : 'click', + dblclick : 'doubleClick', + mousemove : 'mouseMove', + focusin : 'focusIn', + focusout : 'focusOut', + mouseenter : 'mouseEnter', + mouseleave : 'mouseLeave', + submit : 'submit', + input : 'input', + change : 'change', + dragstart : 'dragStart', + drag : 'drag', + dragenter : 'dragEnter', + dragleave : 'dragLeave', + dragover : 'dragOver', + drop : 'drop', + dragend : 'dragEnd' + }, + + /** + @private + + The root DOM element to which event listeners should be attached. Event + listeners will be attached to the document unless this is overridden. + + Can be specified as a DOMElement or a selector string. + + The default body is a string since this may be evaluated before document.body + exists in the DOM. + + @property rootElement + @type DOMElement + @default 'body' + */ + rootElement: 'body', + + /** + @private + + Sets up event listeners for standard browser events. + + This will be called after the browser sends a `DOMContentReady` event. By + default, it will set up all of the listeners on the document body. If you + would like to register the listeners on a different element, set the event + dispatcher's `root` property. + + @method setup + @param addedEvents {Hash} + */ + setup: function(addedEvents, rootElement) { + var event, events = get(this, 'events'); + + Ember.$.extend(events, addedEvents || {}); + + + if (!Ember.isNone(rootElement)) { + set(this, 'rootElement', rootElement); + } + + rootElement = Ember.$(get(this, 'rootElement')); + + Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); + Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); + Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length); + + rootElement.addClass('ember-application'); + + Ember.assert('Unable to add "ember-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ember-application')); + + for (event in events) { + if (events.hasOwnProperty(event)) { + this.setupHandler(rootElement, event, events[event]); + } + } + }, + + /** + @private + + Registers an event listener on the document. If the given event is + triggered, the provided event handler will be triggered on the target view. + + If the target view does not implement the event handler, or if the handler + returns `false`, the parent view will be called. The event will continue to + bubble to each successive parent view until it reaches the top. + + For example, to have the `mouseDown` method called on the target view when + a `mousedown` event is received from the browser, do the following: + + ```javascript + setupHandler('mousedown', 'mouseDown'); + ``` + + @method setupHandler + @param {Element} rootElement + @param {String} event the browser-originated event to listen to + @param {String} eventName the name of the method to call on the view + */ + setupHandler: function(rootElement, event, eventName) { + var self = this; + + rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { + return Ember.handleErrors(function() { + var view = Ember.View.views[this.id], + result = true, manager = null; + + manager = self._findNearestEventManager(view,eventName); + + if (manager && manager !== triggeringManager) { + result = self._dispatchEvent(manager, evt, eventName, view); + } else if (view) { + result = self._bubbleEvent(view,evt,eventName); + } else { + evt.stopPropagation(); + } + + return result; + }, this); + }); + + rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { + return Ember.handleErrors(function() { + var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'), + action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; + + // We have to check for action here since in some cases, jQuery will trigger + // an event on `removeChild` (i.e. focusout) after we've already torn down the + // action handlers for the view. + if (action && action.eventName === eventName) { + return action.handler(evt); + } + }, this); + }); + }, + + _findNearestEventManager: function(view, eventName) { + var manager = null; + + while (view) { + manager = get(view, 'eventManager'); + if (manager && manager[eventName]) { break; } + + view = get(view, 'parentView'); + } + + return manager; + }, + + _dispatchEvent: function(object, evt, eventName, view) { + var result = true; + + var handler = object[eventName]; + if (Ember.typeOf(handler) === 'function') { + result = handler.call(object, evt, view); + // Do not preventDefault in eventManagers. + evt.stopPropagation(); + } + else { + result = this._bubbleEvent(view, evt, eventName); + } + + return result; + }, + + _bubbleEvent: function(view, evt, eventName) { + return Ember.run(function() { + return view.handleEvent(eventName, evt); + }); + }, + + destroy: function() { + var rootElement = get(this, 'rootElement'); + Ember.$(rootElement).off('.ember', '**').removeClass('ember-application'); + return this._super(); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +// Add a new named queue for rendering views that happens +// after bindings have synced, and a queue for scheduling actions +// that that should occur after view rendering. +var queues = Ember.run.queues, + indexOf = Ember.ArrayPolyfills.indexOf; +queues.splice(indexOf.call(queues, 'actions')+1, 0, 'render', 'afterRender'); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; + +// Original class declaration and documentation in runtime/lib/controllers/controller.js +// NOTE: It may be possible with YUIDoc to combine docs in two locations + +/** +Additional methods for the ControllerMixin + +@class ControllerMixin +@namespace Ember +*/ +Ember.ControllerMixin.reopen({ + target: null, + namespace: null, + view: null, + container: null, + _childContainers: null, + + init: function() { + this._super(); + set(this, '_childContainers', {}); + }, + + _modelDidChange: Ember.observer(function() { + var containers = get(this, '_childContainers'); + + for (var prop in containers) { + if (!containers.hasOwnProperty(prop)) { continue; } + containers[prop].destroy(); + } + + set(this, '_childContainers', {}); + }, 'model') +}); + +})(); + + + +(function() { + +})(); + + + +(function() { +var states = {}; + +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; +var guidFor = Ember.guidFor; +var a_forEach = Ember.EnumerableUtils.forEach; +var a_addObject = Ember.EnumerableUtils.addObject; + +var childViewsProperty = Ember.computed(function() { + var childViews = this._childViews, ret = Ember.A(), view = this; + + a_forEach(childViews, function(view) { + if (view.isVirtual) { + ret.pushObjects(get(view, 'childViews')); + } else { + ret.push(view); + } + }); + + ret.replace = function (idx, removedCount, addedViews) { + if (view instanceof Ember.ContainerView) { + Ember.deprecate("Manipulating an Ember.ContainerView through its childViews property is deprecated. Please use the ContainerView instance itself as an Ember.MutableArray."); + return view.replace(idx, removedCount, addedViews); + } + throw new Error("childViews is immutable"); + }; + + return ret; +}); + +Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionality can no longer be disabled.", Ember.ENV.VIEW_PRESERVES_CONTEXT !== false); + +/** + Global hash of shared templates. This will automatically be populated + by the build tools so that you can store your Handlebars templates in + separate files that get loaded into JavaScript at buildtime. + + @property TEMPLATES + @for Ember + @type Hash +*/ +Ember.TEMPLATES = {}; + +/** + `Ember.CoreView` is + + @class CoreView + @namespace Ember + @extends Ember.Object + @uses Ember.Evented +*/ + +Ember.CoreView = Ember.Object.extend(Ember.Evented, { + isView: true, + + states: states, + + init: function() { + this._super(); + this.transitionTo('preRender'); + }, + + /** + If the view is currently inserted into the DOM of a parent view, this + property will point to the parent of the view. + + @property parentView + @type Ember.View + @default null + */ + parentView: Ember.computed(function() { + var parent = this._parentView; + + if (parent && parent.isVirtual) { + return get(parent, 'parentView'); + } else { + return parent; + } + }).property('_parentView'), + + state: null, + + _parentView: null, + + // return the current view, not including virtual views + concreteView: Ember.computed(function() { + if (!this.isVirtual) { return this; } + else { return get(this, 'parentView'); } + }).property('parentView'), + + instrumentName: 'core_view', + + instrumentDetails: function(hash) { + hash.object = this.toString(); + }, + + /** + @private + + Invoked by the view system when this view needs to produce an HTML + representation. This method will create a new render buffer, if needed, + then apply any default attributes, such as class names and visibility. + Finally, the `render()` method is invoked, which is responsible for + doing the bulk of the rendering. + + You should not need to override this method; instead, implement the + `template` property, or if you need more control, override the `render` + method. + + @method renderToBuffer + @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is + passed, a default buffer, using the current view's `tagName`, will + be used. + */ + renderToBuffer: function(parentBuffer, bufferOperation) { + var name = 'render.' + this.instrumentName, + details = {}; + + this.instrumentDetails(details); + + return Ember.instrument(name, details, function() { + return this._renderToBuffer(parentBuffer, bufferOperation); + }, this); + }, + + _renderToBuffer: function(parentBuffer, bufferOperation) { + // If this is the top-most view, start a new buffer. Otherwise, + // create a new buffer relative to the original using the + // provided buffer operation (for example, `insertAfter` will + // insert a new buffer after the "parent buffer"). + var tagName = this.tagName; + + if (tagName === null || tagName === undefined) { + tagName = 'div'; + } + + var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || Ember.RenderBuffer(tagName); + this.transitionTo('inBuffer', false); + + this.beforeRender(buffer); + this.render(buffer); + this.afterRender(buffer); + + return buffer; + }, + + /** + @private + + Override the default event firing from `Ember.Evented` to + also call methods with the given name. + + @method trigger + @param name {String} + */ + trigger: function(name) { + this._super.apply(this, arguments); + var method = this[name]; + if (method) { + var args = [], i, l; + for (i = 1, l = arguments.length; i < l; i++) { + args.push(arguments[i]); + } + return method.apply(this, args); + } + }, + + has: function(name) { + return Ember.typeOf(this[name]) === 'function' || this._super(name); + }, + + destroy: function() { + var parent = this._parentView; + + if (!this._super()) { return; } + + // destroy the element -- this will avoid each child view destroying + // the element over and over again... + if (!this.removedFromDOM) { this.destroyElement(); } + + // remove from parent if found. Don't call removeFromParent, + // as removeFromParent will try to remove the element from + // the DOM again. + if (parent) { parent.removeChild(this); } + + this.transitionTo('destroying', false); + + return this; + }, + + clearRenderedChildren: Ember.K, + triggerRecursively: Ember.K, + invokeRecursively: Ember.K, + transitionTo: Ember.K, + destroyElement: Ember.K +}); + +var ViewCollection = Ember._ViewCollection = function(initialViews) { + var views = this.views = initialViews || []; + this.length = views.length; +}; + +ViewCollection.prototype = { + length: 0, + + trigger: function(eventName) { + var views = this.views, view; + for (var i = 0, l = views.length; i < l; i++) { + view = views[i]; + if (view.trigger) { view.trigger(eventName); } + } + }, + + triggerRecursively: function(eventName) { + var views = this.views; + for (var i = 0, l = views.length; i < l; i++) { + views[i].triggerRecursively(eventName); + } + }, + + invokeRecursively: function(fn) { + var views = this.views, view; + + for (var i = 0, l = views.length; i < l; i++) { + view = views[i]; + fn(view); + } + }, + + transitionTo: function(state, children) { + var views = this.views; + for (var i = 0, l = views.length; i < l; i++) { + views[i].transitionTo(state, children); + } + }, + + push: function() { + this.length += arguments.length; + var views = this.views; + return views.push.apply(views, arguments); + }, + + objectAt: function(idx) { + return this.views[idx]; + }, + + forEach: function(callback) { + var views = this.views; + return a_forEach(views, callback); + }, + + clear: function() { + this.length = 0; + this.views.length = 0; + } +}; + +var EMPTY_ARRAY = []; + +/** + `Ember.View` is the class in Ember responsible for encapsulating templates of + HTML content, combining templates with data to render as sections of a page's + DOM, and registering and responding to user-initiated events. + + ## HTML Tag + + The default HTML tag name used for a view's DOM representation is `div`. This + can be customized by setting the `tagName` property. The following view +class: + + ```javascript + ParagraphView = Ember.View.extend({ + tagName: 'em' + }); + ``` + + Would result in instances with the following HTML: + + ```html + <em id="ember1" class="ember-view"></em> + ``` + + ## HTML `class` Attribute + + The HTML `class` attribute of a view's tag can be set by providing a + `classNames` property that is set to an array of strings: + + ```javascript + MyView = Ember.View.extend({ + classNames: ['my-class', 'my-other-class'] + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view my-class my-other-class"></div> + ``` + + `class` attribute values can also be set by providing a `classNameBindings` + property set to an array of properties names for the view. The return value + of these properties will be added as part of the value for the view's `class` + attribute. These properties can be computed properties: + + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['propertyA', 'propertyB'], + propertyA: 'from-a', + propertyB: function(){ + if(someLogic){ return 'from-b'; } + }.property() + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view from-a from-b"></div> + ``` + + If the value of a class name binding returns a boolean the property name + itself will be used as the class name if the property is true. The class name + will not be added if the value is `false` or `undefined`. + + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['hovered'], + hovered: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view hovered"></div> + ``` + + When using boolean class name bindings you can supply a string value other + than the property name for use as the `class` HTML attribute by appending the + preferred value after a ":" character when defining the binding: + + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['awesome:so-very-cool'], + awesome: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view so-very-cool"></div> + ``` + + Boolean value class name bindings whose property names are in a + camelCase-style format will be converted to a dasherized format: + + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['isUrgent'], + isUrgent: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view is-urgent"></div> + ``` + + Class name bindings can also refer to object values that are found by + traversing a path relative to the view itself: + + ```javascript + MyView = Ember.View.extend({ + classNameBindings: ['messages.empty'] + messages: Ember.Object.create({ + empty: true + }) + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view empty"></div> + ``` + + If you want to add a class name for a property which evaluates to true and + and a different class name if it evaluates to false, you can pass a binding + like this: + + ```javascript + // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false + Ember.View.create({ + classNameBindings: ['isEnabled:enabled:disabled'] + isEnabled: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view enabled"></div> + ``` + + When isEnabled is `false`, the resulting HTML reprensentation looks like + this: + + ```html + <div id="ember1" class="ember-view disabled"></div> + ``` + + This syntax offers the convenience to add a class if a property is `false`: + + ```javascript + // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false + Ember.View.create({ + classNameBindings: ['isEnabled::disabled'] + isEnabled: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view"></div> + ``` + + When the `isEnabled` property on the view is set to `false`, it will result + in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view disabled"></div> + ``` + + Updates to the the value of a class name binding will result in automatic + update of the HTML `class` attribute in the view's rendered HTML + representation. If the value becomes `false` or `undefined` the class name + will be removed. + + Both `classNames` and `classNameBindings` are concatenated properties. See + `Ember.Object` documentation for more information about concatenated + properties. + + ## HTML Attributes + + The HTML attribute section of a view's tag can be set by providing an + `attributeBindings` property set to an array of property names on the view. + The return value of these properties will be used as the value of the view's + HTML associated attribute: + + ```javascript + AnchorView = Ember.View.extend({ + tagName: 'a', + attributeBindings: ['href'], + href: 'http://google.com' + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <a id="ember1" class="ember-view" href="http://google.com"></a> + ``` + + If the return value of an `attributeBindings` monitored property is a boolean + the property will follow HTML's pattern of repeating the attribute's name as + its value: + + ```javascript + MyTextInput = Ember.View.extend({ + tagName: 'input', + attributeBindings: ['disabled'], + disabled: true + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <input id="ember1" class="ember-view" disabled="disabled" /> + ``` + + `attributeBindings` can refer to computed properties: + + ```javascript + MyTextInput = Ember.View.extend({ + tagName: 'input', + attributeBindings: ['disabled'], + disabled: function(){ + if (someLogic) { + return true; + } else { + return false; + } + }.property() + }); + ``` + + Updates to the the property of an attribute binding will result in automatic + update of the HTML attribute in the view's rendered HTML representation. + + `attributeBindings` is a concatenated property. See `Ember.Object` + documentation for more information about concatenated properties. + + ## Templates + + The HTML contents of a view's rendered representation are determined by its + template. Templates can be any function that accepts an optional context + parameter and returns a string of HTML that will be inserted within the + view's tag. Most typically in Ember this function will be a compiled + `Ember.Handlebars` template. + + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('I am the template') + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view">I am the template</div> + ``` + + Within an Ember application is more common to define a Handlebars templates as + part of a page: + + ```html + <script type='text/x-handlebars' data-template-name='some-template'> + Hello + </script> + ``` + + And associate it by name using a view's `templateName` property: + + ```javascript + AView = Ember.View.extend({ + templateName: 'some-template' + }); + ``` + + Using a value for `templateName` that does not have a Handlebars template + with a matching `data-template-name` attribute will throw an error. + + Assigning a value to both `template` and `templateName` properties will throw + an error. + + For views classes that may have a template later defined (e.g. as the block + portion of a `{{view}}` Handlebars helper call in another template or in + a subclass), you can provide a `defaultTemplate` property set to compiled + template function. If a template is not later provided for the view instance + the `defaultTemplate` value will be used: + + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default'), + template: null, + templateName: null + }); + ``` + + Will result in instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view">I was the default</div> + ``` + + If a `template` or `templateName` is provided it will take precedence over + `defaultTemplate`: + + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default') + }); + + aView = AView.create({ + template: Ember.Handlebars.compile('I was the template, not default') + }); + ``` + + Will result in the following HTML representation when rendered: + + ```html + <div id="ember1" class="ember-view">I was the template, not default</div> + ``` + + ## View Context + + The default context of the compiled template is the view's controller: + + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') + }); + + aController = Ember.Object.create({ + firstName: 'Barry', + excitedGreeting: function(){ + return this.get("content.firstName") + "!!!" + }.property() + }); + + aView = AView.create({ + controller: aController, + }); + ``` + + Will result in an HTML representation of: + + ```html + <div id="ember1" class="ember-view">Hello Barry!!!</div> + ``` + + A context can also be explicitly supplied through the view's `context` + property. If the view has neither `context` nor `controller` properties, the + `parentView`'s context will be used. + + ## Layouts + + Views can have a secondary template that wraps their main template. Like + primary templates, layouts can be any function that accepts an optional + context parameter and returns a string of HTML that will be inserted inside + view's tag. Views whose HTML element is self closing (e.g. `<input />`) + cannot have a layout and this property will be ignored. + + Most typically in Ember a layout will be a compiled `Ember.Handlebars` + template. + + A view's layout can be set directly with the `layout` property or reference + an existing Handlebars template by name with the `layoutName` property. + + A template used as a layout must contain a single use of the Handlebars + `{{yield}}` helper. The HTML contents of a view's rendered `template` will be + inserted at this location: + + ```javascript + AViewWithLayout = Ember.View.extend({ + layout: Ember.Handlebars.compile("<div class='my-decorative-class'>{{yield}}</div>") + template: Ember.Handlebars.compile("I got wrapped"), + }); + ``` + + Will result in view instances with an HTML representation of: + + ```html + <div id="ember1" class="ember-view"> + <div class="my-decorative-class"> + I got wrapped + </div> + </div> + ``` + + See `Handlebars.helpers.yield` for more information. + + ## Responding to Browser Events + + Views can respond to user-initiated events in one of three ways: method + implementation, through an event manager, and through `{{action}}` helper use + in their template or layout. + + ### Method Implementation + + Views can respond to user-initiated events by implementing a method that + matches the event name. A `jQuery.Event` object will be passed as the + argument to this method. + + ```javascript + AView = Ember.View.extend({ + click: function(event){ + // will be called when when an instance's + // rendered element is clicked + } + }); + ``` + + ### Event Managers + + Views can define an object as their `eventManager` property. This object can + then implement methods that match the desired event names. Matching events + that occur on the view's rendered HTML or the rendered HTML of any of its DOM + descendants will trigger this method. A `jQuery.Event` object will be passed + as the first argument to the method and an `Ember.View` object as the + second. The `Ember.View` will be the view whose rendered HTML was interacted + with. This may be the view with the `eventManager` property or one of its + descendent views. + + ```javascript + AView = Ember.View.extend({ + eventManager: Ember.Object.create({ + doubleClick: function(event, view){ + // will be called when when an instance's + // rendered element or any rendering + // of this views's descendent + // elements is clicked + } + }) + }); + ``` + + An event defined for an event manager takes precedence over events of the + same name handled through methods on the view. + + ```javascript + AView = Ember.View.extend({ + mouseEnter: function(event){ + // will never trigger. + }, + eventManager: Ember.Object.create({ + mouseEnter: function(event, view){ + // takes presedence over AView#mouseEnter + } + }) + }); + ``` + + Similarly a view's event manager will take precedence for events of any views + rendered as a descendent. A method name that matches an event name will not + be called if the view instance was rendered inside the HTML representation of + a view that has an `eventManager` property defined that handles events of the + name. Events not handled by the event manager will still trigger method calls + on the descendent. + + ```javascript + OuterView = Ember.View.extend({ + template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), + eventManager: Ember.Object.create({ + mouseEnter: function(event, view){ + // view might be instance of either + // OuterView or InnerView depending on + // where on the page the user interaction occured + } + }) + }); + + InnerView = Ember.View.extend({ + click: function(event){ + // will be called if rendered inside + // an OuterView because OuterView's + // eventManager doesn't handle click events + }, + mouseEnter: function(event){ + // will never be called if rendered inside + // an OuterView. + } + }); + ``` + + ### Handlebars `{{action}}` Helper + + See `Handlebars.helpers.action`. + + ### Event Names + + All of the event handling approaches described above respond to the same set + of events. The names of the built-in events are listed below. (The hash of + built-in events exists in `Ember.EventDispatcher`.) Additional, custom events + can be registered by using `Ember.Application.customEvents`. + + Touch events: + + * `touchStart` + * `touchMove` + * `touchEnd` + * `touchCancel` + + Keyboard events + + * `keyDown` + * `keyUp` + * `keyPress` + + Mouse events + + * `mouseDown` + * `mouseUp` + * `contextMenu` + * `click` + * `doubleClick` + * `mouseMove` + * `focusIn` + * `focusOut` + * `mouseEnter` + * `mouseLeave` + + Form events: + + * `submit` + * `change` + * `focusIn` + * `focusOut` + * `input` + + HTML5 drag and drop events: + + * `dragStart` + * `drag` + * `dragEnter` + * `dragLeave` + * `drop` + * `dragEnd` + + ## Handlebars `{{view}}` Helper + + Other `Ember.View` instances can be included as part of a view's template by + using the `{{view}}` Handlebars helper. See `Handlebars.helpers.view` for + additional information. + + @class View + @namespace Ember + @extends Ember.CoreView +*/ +Ember.View = Ember.CoreView.extend( +/** @scope Ember.View.prototype */ { + + concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], + + /** + @property isView + @type Boolean + @default true + @final + */ + isView: true, + + // .......................................................... + // TEMPLATE SUPPORT + // + + /** + The name of the template to lookup if no template is provided. + + `Ember.View` will look for a template with this name in this view's + `templates` object. By default, this will be a global object + shared in `Ember.TEMPLATES`. + + @property templateName + @type String + @default null + */ + templateName: null, + + /** + The name of the layout to lookup if no layout is provided. + + `Ember.View` will look for a template with this name in this view's + `templates` object. By default, this will be a global object + shared in `Ember.TEMPLATES`. + + @property layoutName + @type String + @default null + */ + layoutName: null, + + /** + The hash in which to look for `templateName`. + + @property templates + @type Ember.Object + @default Ember.TEMPLATES + */ + templates: Ember.TEMPLATES, + + /** + The template used to render the view. This should be a function that + accepts an optional context parameter and returns a string of HTML that + will be inserted into the DOM relative to its parent view. + + In general, you should set the `templateName` property instead of setting + the template yourself. + + @property template + @type Function + */ + template: Ember.computed(function(key, value) { + if (value !== undefined) { return value; } + + var templateName = get(this, 'templateName'), + template = this.templateForName(templateName, 'template'); + + Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); + + return template || get(this, 'defaultTemplate'); + }).property('templateName'), + + /** + The controller managing this view. If this property is set, it will be + made available for use by the template. + + @property controller + @type Object + */ + controller: Ember.computed(function(key) { + var parentView = get(this, '_parentView'); + return parentView ? get(parentView, 'controller') : null; + }).property('_parentView'), + + /** + A view may contain a layout. A layout is a regular template but + supersedes the `template` property during rendering. It is the + responsibility of the layout template to retrieve the `template` + property from the view (or alternatively, call `Handlebars.helpers.yield`, + `{{yield}}`) to render it in the correct location. + + This is useful for a view that has a shared wrapper, but which delegates + the rendering of the contents of the wrapper to the `template` property + on a subclass. + + @property layout + @type Function + */ + layout: Ember.computed(function(key) { + var layoutName = get(this, 'layoutName'), + layout = this.templateForName(layoutName, 'layout'); + + Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); + + return layout || get(this, 'defaultLayout'); + }).property('layoutName'), + + templateForName: function(name, type) { + if (!name) { return; } + Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); + + // the defaultContainer is deprecated + var container = this.container || (Ember.Container && Ember.Container.defaultContainer); + return container && container.lookup('template:' + name); + }, + + /** + The object from which templates should access properties. + + This object will be passed to the template function each time the render + method is called, but it is up to the individual function to decide what + to do with it. + + By default, this will be the view's controller. + + @property context + @type Object + */ + context: Ember.computed(function(key, value) { + if (arguments.length === 2) { + set(this, '_context', value); + return value; + } else { + return get(this, '_context'); + } + }).volatile(), + + /** + @private + + Private copy of the view's template context. This can be set directly + by Handlebars without triggering the observer that causes the view + to be re-rendered. + + The context of a view is looked up as follows: + + 1. Supplied context (usually by Handlebars) + 2. Specified controller + 3. `parentView`'s context (for a child of a ContainerView) + + The code in Handlebars that overrides the `_context` property first + checks to see whether the view has a specified controller. This is + something of a hack and should be revisited. + + @property _context + */ + _context: Ember.computed(function(key) { + var parentView, controller; + + if (controller = get(this, 'controller')) { + return controller; + } + + parentView = this._parentView; + if (parentView) { + return get(parentView, '_context'); + } + + return null; + }), + + /** + @private + + If a value that affects template rendering changes, the view should be + re-rendered to reflect the new value. + + @method _contextDidChange + */ + _contextDidChange: Ember.observer(function() { + this.rerender(); + }, 'context'), + + /** + If `false`, the view will appear hidden in DOM. + + @property isVisible + @type Boolean + @default null + */ + isVisible: true, + + /** + @private + + Array of child views. You should never edit this array directly. + Instead, use `appendChild` and `removeFromParent`. + + @property childViews + @type Array + @default [] + */ + childViews: childViewsProperty, + + _childViews: EMPTY_ARRAY, + + // When it's a virtual view, we need to notify the parent that their + // childViews will change. + _childViewsWillChange: Ember.beforeObserver(function() { + if (this.isVirtual) { + var parentView = get(this, 'parentView'); + if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); } + } + }, 'childViews'), + + // When it's a virtual view, we need to notify the parent that their + // childViews did change. + _childViewsDidChange: Ember.observer(function() { + if (this.isVirtual) { + var parentView = get(this, 'parentView'); + if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); } + } + }, 'childViews'), + + /** + Return the nearest ancestor that is an instance of the provided + class. + + @property nearestInstanceOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View + @deprecated + */ + nearestInstanceOf: function(klass) { + Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); + var view = get(this, 'parentView'); + + while (view) { + if(view instanceof klass) { return view; } + view = get(view, 'parentView'); + } + }, + + /** + Return the nearest ancestor that is an instance of the provided + class or mixin. + + @property nearestOfType + @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), + or an instance of Ember.Mixin. + @return Ember.View + */ + nearestOfType: function(klass) { + var view = get(this, 'parentView'), + isOfType = klass instanceof Ember.Mixin ? + function(view) { return klass.detect(view); } : + function(view) { return klass.detect(view.constructor); }; + + while (view) { + if( isOfType(view) ) { return view; } + view = get(view, 'parentView'); + } + }, + + /** + Return the nearest ancestor that has a given property. + + @property nearestWithProperty + @param {String} property A property name + @return Ember.View + */ + nearestWithProperty: function(property) { + var view = get(this, 'parentView'); + + while (view) { + if (property in view) { return view; } + view = get(view, 'parentView'); + } + }, + + /** + Return the nearest ancestor whose parent is an instance of + `klass`. + + @property nearestChildOf + @param {Class} klass Subclass of Ember.View (or Ember.View itself) + @return Ember.View + */ + nearestChildOf: function(klass) { + var view = get(this, 'parentView'); + + while (view) { + if(get(view, 'parentView') instanceof klass) { return view; } + view = get(view, 'parentView'); + } + }, + + /** + @private + + When the parent view changes, recursively invalidate `controller` + + @method _parentViewDidChange + */ + _parentViewDidChange: Ember.observer(function() { + if (this.isDestroying) { return; } + + this.trigger('parentViewDidChange'); + + if (get(this, 'parentView.controller') && !get(this, 'controller')) { + this.notifyPropertyChange('controller'); + } + }, '_parentView'), + + _controllerDidChange: Ember.observer(function() { + if (this.isDestroying) { return; } + + this.rerender(); + + this.forEachChildView(function(view) { + view.propertyDidChange('controller'); + }); + }, 'controller'), + + cloneKeywords: function() { + var templateData = get(this, 'templateData'); + + var keywords = templateData ? Ember.copy(templateData.keywords) : {}; + set(keywords, 'view', get(this, 'concreteView')); + set(keywords, '_view', this); + set(keywords, 'controller', get(this, 'controller')); + + return keywords; + }, + + /** + Called on your view when it should push strings of HTML into a + `Ember.RenderBuffer`. Most users will want to override the `template` + or `templateName` properties instead of this method. + + By default, `Ember.View` will look for a function in the `template` + property and invoke it with the value of `context`. The value of + `context` will be the view's controller unless you override it. + + @method render + @param {Ember.RenderBuffer} buffer The render buffer + */ + render: function(buffer) { + // If this view has a layout, it is the responsibility of the + // the layout to render the view's template. Otherwise, render the template + // directly. + var template = get(this, 'layout') || get(this, 'template'); + + if (template) { + var context = get(this, 'context'); + var keywords = this.cloneKeywords(); + var output; + + var data = { + view: this, + buffer: buffer, + isRenderData: true, + keywords: keywords, + insideGroup: get(this, 'templateData.insideGroup') + }; + + // Invoke the template with the provided template context, which + // is the view's controller by default. A hash of data is also passed that provides + // the template with access to the view and render buffer. + + Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); + // The template should write directly to the render buffer instead + // of returning a string. + output = template(context, { data: data }); + + // If the template returned a string instead of writing to the buffer, + // push the string onto the buffer. + if (output !== undefined) { buffer.push(output); } + } + }, + + /** + Renders the view again. This will work regardless of whether the + view is already in the DOM or not. If the view is in the DOM, the + rendering process will be deferred to give bindings a chance + to synchronize. + + If children were added during the rendering process using `appendChild`, + `rerender` will remove them, because they will be added again + if needed by the next `render`. + + In general, if the display of your view changes, you should modify + the DOM element directly instead of manually calling `rerender`, which can + be slow. + + @method rerender + */ + rerender: function() { + return this.currentState.rerender(this); + }, + + clearRenderedChildren: function() { + var lengthBefore = this.lengthBeforeRender, + lengthAfter = this.lengthAfterRender; + + // If there were child views created during the last call to render(), + // remove them under the assumption that they will be re-created when + // we re-render. + + // VIEW-TODO: Unit test this path. + var childViews = this._childViews; + for (var i=lengthAfter-1; i>=lengthBefore; i--) { + if (childViews[i]) { childViews[i].destroy(); } + } + }, + + /** + @private + + Iterates over the view's `classNameBindings` array, inserts the value + of the specified property into the `classNames` array, then creates an + observer to update the view's element if the bound property ever changes + in the future. + + @method _applyClassNameBindings + */ + _applyClassNameBindings: function(classBindings) { + var classNames = this.classNames, + elem, newClass, dasherizedClass; + + // Loop through all of the configured bindings. These will be either + // property names ('isUrgent') or property paths relative to the view + // ('content.isUrgent') + a_forEach(classBindings, function(binding) { + + // Variable in which the old class value is saved. The observer function + // closes over this variable, so it knows which string to remove when + // the property changes. + var oldClass; + // Extract just the property name from bindings like 'foo:bar' + var parsedPath = Ember.View._parsePropertyPath(binding); + + // Set up an observer on the context. If the property changes, toggle the + // class name. + var observer = function() { + // Get the current value of the property + newClass = this._classStringForProperty(binding); + elem = this.$(); + + // If we had previously added a class to the element, remove it. + if (oldClass) { + elem.removeClass(oldClass); + // Also remove from classNames so that if the view gets rerendered, + // the class doesn't get added back to the DOM. + classNames.removeObject(oldClass); + } + + // If necessary, add a new class. Make sure we keep track of it so + // it can be removed in the future. + if (newClass) { + elem.addClass(newClass); + oldClass = newClass; + } else { + oldClass = null; + } + }; + + // Get the class name for the property at its current value + dasherizedClass = this._classStringForProperty(binding); + + if (dasherizedClass) { + // Ensure that it gets into the classNames array + // so it is displayed when we render. + a_addObject(classNames, dasherizedClass); + + // Save a reference to the class name so we can remove it + // if the observer fires. Remember that this variable has + // been closed over by the observer. + oldClass = dasherizedClass; + } + + this.registerObserver(this, parsedPath.path, observer); + // Remove className so when the view is rerendered, + // the className is added based on binding reevaluation + this.one('willClearRender', function() { + if (oldClass) { + classNames.removeObject(oldClass); + oldClass = null; + } + }); + + }, this); + }, + + /** + @private + + Iterates through the view's attribute bindings, sets up observers for each, + then applies the current value of the attributes to the passed render buffer. + + @method _applyAttributeBindings + @param {Ember.RenderBuffer} buffer + */ + _applyAttributeBindings: function(buffer, attributeBindings) { + var attributeValue, elem; + + a_forEach(attributeBindings, function(binding) { + var split = binding.split(':'), + property = split[0], + attributeName = split[1] || property; + + // Create an observer to add/remove/change the attribute if the + // JavaScript property changes. + var observer = function() { + elem = this.$(); + + attributeValue = get(this, property); + + Ember.View.applyAttributeBindings(elem, attributeName, attributeValue); + }; + + this.registerObserver(this, property, observer); + + // Determine the current value and add it to the render buffer + // if necessary. + attributeValue = get(this, property); + Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue); + }, this); + }, + + /** + @private + + Given a property name, returns a dasherized version of that + property name if the property evaluates to a non-falsy value. + + For example, if the view has property `isUrgent` that evaluates to true, + passing `isUrgent` to this method will return `"is-urgent"`. + + @method _classStringForProperty + @param property + */ + _classStringForProperty: function(property) { + var parsedPath = Ember.View._parsePropertyPath(property); + var path = parsedPath.path; + + var val = get(this, path); + if (val === undefined && Ember.isGlobalPath(path)) { + val = get(Ember.lookup, path); + } + + return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + }, + + // .......................................................... + // ELEMENT SUPPORT + // + + /** + Returns the current DOM element for the view. + + @property element + @type DOMElement + */ + element: Ember.computed(function(key, value) { + if (value !== undefined) { + return this.currentState.setElement(this, value); + } else { + return this.currentState.getElement(this); + } + }).property('_parentView'), + + /** + Returns a jQuery object for this view's element. If you pass in a selector + string, this method will return a jQuery object, using the current element + as its buffer. + + For example, calling `view.$('li')` will return a jQuery object containing + all of the `li` elements inside the DOM element of this view. + + @method $ + @param {String} [selector] a jQuery-compatible selector string + @return {jQuery} the jQuery object for the DOM node + */ + $: function(sel) { + return this.currentState.$(this, sel); + }, + + mutateChildViews: function(callback) { + var childViews = this._childViews, + idx = childViews.length, + view; + + while(--idx >= 0) { + view = childViews[idx]; + callback(this, view, idx); + } + + return this; + }, + + forEachChildView: function(callback) { + var childViews = this._childViews; + + if (!childViews) { return this; } + + var len = childViews.length, + view, idx; + + for (idx = 0; idx < len; idx++) { + view = childViews[idx]; + callback(view); + } + + return this; + }, + + /** + Appends the view's element to the specified parent element. + + If the view does not have an HTML representation yet, `createElement()` + will be called automatically. + + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the given element until all bindings have + finished synchronizing. + + This is not typically a function that you will need to call directly when + building your application. You might consider using `Ember.ContainerView` + instead. If you do need to use `appendTo`, be sure that the target element + you are providing is associated with an `Ember.Application` and does not + have an ancestor element that is associated with an Ember view. + + @method appendTo + @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object + @return {Ember.View} receiver + */ + appendTo: function(target) { + // Schedule the DOM element to be created and appended to the given + // element after bindings have synchronized. + this._insertElementLater(function() { + Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); + this.$().appendTo(target); + }); + + return this; + }, + + /** + Replaces the content of the specified parent element with this view's + element. If the view does not have an HTML representation yet, + `createElement()` will be called automatically. + + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the given element until all bindings have + finished synchronizing + + @method replaceIn + @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object + @return {Ember.View} received + */ + replaceIn: function(target) { + Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); + + this._insertElementLater(function() { + Ember.$(target).empty(); + this.$().appendTo(target); + }); + + return this; + }, + + /** + @private + + Schedules a DOM operation to occur during the next render phase. This + ensures that all bindings have finished synchronizing before the view is + rendered. + + To use, pass a function that performs a DOM operation. + + Before your function is called, this view and all child views will receive + the `willInsertElement` event. After your function is invoked, this view + and all of its child views will receive the `didInsertElement` event. + + ```javascript + view._insertElementLater(function() { + this.createElement(); + this.$().appendTo('body'); + }); + ``` + + @method _insertElementLater + @param {Function} fn the function that inserts the element into the DOM + */ + _insertElementLater: function(fn) { + this._scheduledInsert = Ember.run.scheduleOnce('render', this, '_insertElement', fn); + }, + + _insertElement: function (fn) { + this._scheduledInsert = null; + this.currentState.insertElement(this, fn); + }, + + /** + Appends the view's element to the document body. If the view does + not have an HTML representation yet, `createElement()` will be called + automatically. + + If your application uses the `rootElement` property, you must append + the view within that element. Rendering views outside of the `rootElement` + is not supported. + + Note that this method just schedules the view to be appended; the DOM + element will not be appended to the document body until all bindings have + finished synchronizing. + + @method append + @return {Ember.View} receiver + */ + append: function() { + return this.appendTo(document.body); + }, + + /** + Removes the view's element from the element to which it is attached. + + @method remove + @return {Ember.View} receiver + */ + remove: function() { + // What we should really do here is wait until the end of the run loop + // to determine if the element has been re-appended to a different + // element. + // In the interim, we will just re-render if that happens. It is more + // important than elements get garbage collected. + if (!this.removedFromDOM) { this.destroyElement(); } + this.invokeRecursively(function(view) { + if (view.clearRenderedChildren) { view.clearRenderedChildren(); } + }); + }, + + elementId: null, + + /** + Attempts to discover the element in the parent element. The default + implementation looks for an element with an ID of `elementId` (or the + view's guid if `elementId` is null). You can override this method to + provide your own form of lookup. For example, if you want to discover your + element using a CSS class name instead of an ID. + + @method findElementInParentElement + @param {DOMElement} parentElement The parent's DOM element + @return {DOMElement} The discovered element + */ + findElementInParentElement: function(parentElem) { + var id = "#" + this.elementId; + return Ember.$(id)[0] || Ember.$(id, parentElem)[0]; + }, + + /** + Creates a DOM representation of the view and all of its + child views by recursively calling the `render()` method. + + After the element has been created, `didInsertElement` will + be called on this view and all of its child views. + + @method createElement + @return {Ember.View} receiver + */ + createElement: function() { + if (get(this, 'element')) { return this; } + + var buffer = this.renderToBuffer(); + set(this, 'element', buffer.element()); + + return this; + }, + + /** + Called when a view is going to insert an element into the DOM. + + @event willInsertElement + */ + willInsertElement: Ember.K, + + /** + Called when the element of the view has been inserted into the DOM + or after the view was re-rendered. Override this function to do any + set up that requires an element in the document body. + + @event didInsertElement + */ + didInsertElement: Ember.K, + + /** + Called when the view is about to rerender, but before anything has + been torn down. This is a good opportunity to tear down any manual + observers you have installed based on the DOM state + + @event willClearRender + */ + willClearRender: Ember.K, + + /** + @private + + Run this callback on the current view (unless includeSelf is false) and recursively on child views. + + @method invokeRecursively + @param fn {Function} + @param includeSelf (optional, default true) + */ + invokeRecursively: function(fn, includeSelf) { + var childViews = (includeSelf === false) ? this._childViews : [this]; + var currentViews, view; + + while (childViews.length) { + currentViews = childViews.slice(); + childViews = []; + + for (var i=0, l=currentViews.length; i<l; i++) { + view = currentViews[i]; + fn(view); + if (view._childViews) { + childViews.push.apply(childViews, view._childViews); + } + } + } + }, + + triggerRecursively: function(eventName) { + var childViews = [this], currentViews, view; + + while (childViews.length) { + currentViews = childViews.slice(); + childViews = []; + + for (var i=0, l=currentViews.length; i<l; i++) { + view = currentViews[i]; + if (view.trigger) { view.trigger(eventName); } + if (view._childViews) { + childViews.push.apply(childViews, view._childViews); + } + } + } + }, + + viewHierarchyCollection: function() { + var currentView, viewCollection = new ViewCollection([this]); + + for (var i = 0; i < viewCollection.length; i++) { + currentView = viewCollection.objectAt(i); + if (currentView._childViews) { + viewCollection.push.apply(viewCollection, currentView._childViews); + } + } + + return viewCollection; + }, + + /** + Destroys any existing element along with the element for any child views + as well. If the view does not currently have a element, then this method + will do nothing. + + If you implement `willDestroyElement()` on your view, then this method will + be invoked on your view before your element is destroyed to give you a + chance to clean up any event handlers, etc. + + If you write a `willDestroyElement()` handler, you can assume that your + `didInsertElement()` handler was called earlier for the same element. + + Normally you will not call or override this method yourself, but you may + want to implement the above callbacks when it is run. + + @method destroyElement + @return {Ember.View} receiver + */ + destroyElement: function() { + return this.currentState.destroyElement(this); + }, + + /** + Called when the element of the view is going to be destroyed. Override + this function to do any teardown that requires an element, like removing + event listeners. + + @event willDestroyElement + */ + willDestroyElement: function() {}, + + /** + @private + + Triggers the `willDestroyElement` event (which invokes the + `willDestroyElement()` method if it exists) on this view and all child + views. + + Before triggering `willDestroyElement`, it first triggers the + `willClearRender` event recursively. + + @method _notifyWillDestroyElement + */ + _notifyWillDestroyElement: function() { + var viewCollection = this.viewHierarchyCollection(); + viewCollection.trigger('willClearRender'); + viewCollection.trigger('willDestroyElement'); + return viewCollection; + }, + + _elementWillChange: Ember.beforeObserver(function() { + this.forEachChildView(function(view) { + Ember.propertyWillChange(view, 'element'); + }); + }, 'element'), + + /** + @private + + If this view's element changes, we need to invalidate the caches of our + child views so that we do not retain references to DOM elements that are + no longer needed. + + @method _elementDidChange + */ + _elementDidChange: Ember.observer(function() { + this.forEachChildView(function(view) { + Ember.propertyDidChange(view, 'element'); + }); + }, 'element'), + + /** + Called when the parentView property has changed. + + @event parentViewDidChange + */ + parentViewDidChange: Ember.K, + + instrumentName: 'view', + + instrumentDetails: function(hash) { + hash.template = get(this, 'templateName'); + this._super(hash); + }, + + _renderToBuffer: function(parentBuffer, bufferOperation) { + this.lengthBeforeRender = this._childViews.length; + var buffer = this._super(parentBuffer, bufferOperation); + this.lengthAfterRender = this._childViews.length; + + return buffer; + }, + + renderToBufferIfNeeded: function (buffer) { + return this.currentState.renderToBufferIfNeeded(this, buffer); + }, + + beforeRender: function(buffer) { + this.applyAttributesToBuffer(buffer); + buffer.pushOpeningTag(); + }, + + afterRender: function(buffer) { + buffer.pushClosingTag(); + }, + + applyAttributesToBuffer: function(buffer) { + // Creates observers for all registered class name and attribute bindings, + // then adds them to the element. + var classNameBindings = get(this, 'classNameBindings'); + if (classNameBindings.length) { + this._applyClassNameBindings(classNameBindings); + } + + // Pass the render buffer so the method can apply attributes directly. + // This isn't needed for class name bindings because they use the + // existing classNames infrastructure. + var attributeBindings = get(this, 'attributeBindings'); + if (attributeBindings.length) { + this._applyAttributeBindings(buffer, attributeBindings); + } + + buffer.setClasses(this.classNames); + buffer.id(this.elementId); + + var role = get(this, 'ariaRole'); + if (role) { + buffer.attr('role', role); + } + + if (get(this, 'isVisible') === false) { + buffer.style('display', 'none'); + } + }, + + // .......................................................... + // STANDARD RENDER PROPERTIES + // + + /** + Tag name for the view's outer element. The tag name is only used when an + element is first created. If you change the `tagName` for an element, you + must destroy and recreate the view element. + + By default, the render buffer will use a `<div>` tag for views. + + @property tagName + @type String + @default null + */ + + // We leave this null by default so we can tell the difference between + // the default case and a user-specified tag. + tagName: null, + + /** + The WAI-ARIA role of the control represented by this view. For example, a + button may have a role of type 'button', or a pane may have a role of + type 'alertdialog'. This property is used by assistive software to help + visually challenged users navigate rich web applications. + + The full list of valid WAI-ARIA roles is available at: + http://www.w3.org/TR/wai-aria/roles#roles_categorization + + @property ariaRole + @type String + @default null + */ + ariaRole: null, + + /** + Standard CSS class names to apply to the view's outer element. This + property automatically inherits any class names defined by the view's + superclasses as well. + + @property classNames + @type Array + @default ['ember-view'] + */ + classNames: ['ember-view'], + + /** + A list of properties of the view to apply as class names. If the property + is a string value, the value of that string will be applied as a class + name. + + ```javascript + // Applies the 'high' class to the view element + Ember.View.create({ + classNameBindings: ['priority'] + priority: 'high' + }); + ``` + + If the value of the property is a Boolean, the name of that property is + added as a dasherized class name. + + ```javascript + // Applies the 'is-urgent' class to the view element + Ember.View.create({ + classNameBindings: ['isUrgent'] + isUrgent: true + }); + ``` + + If you would prefer to use a custom value instead of the dasherized + property name, you can pass a binding like this: + + ```javascript + // Applies the 'urgent' class to the view element + Ember.View.create({ + classNameBindings: ['isUrgent:urgent'] + isUrgent: true + }); + ``` + + This list of properties is inherited from the view's superclasses as well. + + @property classNameBindings + @type Array + @default [] + */ + classNameBindings: EMPTY_ARRAY, + + /** + A list of properties of the view to apply as attributes. If the property is + a string value, the value of that string will be applied as the attribute. + + ```javascript + // Applies the type attribute to the element + // with the value "button", like <div type="button"> + Ember.View.create({ + attributeBindings: ['type'], + type: 'button' + }); + ``` + + If the value of the property is a Boolean, the name of that property is + added as an attribute. + + ```javascript + // Renders something like <div enabled="enabled"> + Ember.View.create({ + attributeBindings: ['enabled'], + enabled: true + }); + ``` + + @property attributeBindings + */ + attributeBindings: EMPTY_ARRAY, + + // ....................................................... + // CORE DISPLAY METHODS + // + + /** + @private + + Setup a view, but do not finish waking it up. + - configure `childViews` + - register the view with the global views hash, which is used for event + dispatch + + @method init + */ + init: function() { + this.elementId = this.elementId || guidFor(this); + + this._super(); + + // setup child views. be sure to clone the child views array first + this._childViews = this._childViews.slice(); + + Ember.assert("Only arrays are allowed for 'classNameBindings'", Ember.typeOf(this.classNameBindings) === 'array'); + this.classNameBindings = Ember.A(this.classNameBindings.slice()); + + Ember.assert("Only arrays are allowed for 'classNames'", Ember.typeOf(this.classNames) === 'array'); + this.classNames = Ember.A(this.classNames.slice()); + + var viewController = get(this, 'viewController'); + if (viewController) { + viewController = get(viewController); + if (viewController) { + set(viewController, 'view', this); + } + } + }, + + appendChild: function(view, options) { + return this.currentState.appendChild(this, view, options); + }, + + /** + Removes the child view from the parent view. + + @method removeChild + @param {Ember.View} view + @return {Ember.View} receiver + */ + removeChild: function(view) { + // If we're destroying, the entire subtree will be + // freed, and the DOM will be handled separately, + // so no need to mess with childViews. + if (this.isDestroying) { return; } + + // update parent node + set(view, '_parentView', null); + + // remove view from childViews array. + var childViews = this._childViews; + + Ember.EnumerableUtils.removeObject(childViews, view); + + this.propertyDidChange('childViews'); // HUH?! what happened to will change? + + return this; + }, + + /** + Removes all children from the `parentView`. + + @method removeAllChildren + @return {Ember.View} receiver + */ + removeAllChildren: function() { + return this.mutateChildViews(function(parentView, view) { + parentView.removeChild(view); + }); + }, + + destroyAllChildren: function() { + return this.mutateChildViews(function(parentView, view) { + view.destroy(); + }); + }, + + /** + Removes the view from its `parentView`, if one is found. Otherwise + does nothing. + + @method removeFromParent + @return {Ember.View} receiver + */ + removeFromParent: function() { + var parent = this._parentView; + + // Remove DOM element from parent + this.remove(); + + if (parent) { parent.removeChild(this); } + return this; + }, + + /** + You must call `destroy` on a view to destroy the view (and all of its + child views). This will remove the view from any parent node, then make + sure that the DOM element managed by the view can be released by the + memory manager. + + @method destroy + */ + destroy: function() { + var childViews = this._childViews, + // get parentView before calling super because it'll be destroyed + nonVirtualParentView = get(this, 'parentView'), + viewName = this.viewName, + childLen, i; + + if (!this._super()) { return; } + + childLen = childViews.length; + for (i=childLen-1; i>=0; i--) { + childViews[i].removedFromDOM = true; + } + + // remove from non-virtual parent view if viewName was specified + if (viewName && nonVirtualParentView) { + nonVirtualParentView.set(viewName, null); + } + + childLen = childViews.length; + for (i=childLen-1; i>=0; i--) { + childViews[i].destroy(); + } + + return this; + }, + + /** + Instantiates a view to be added to the childViews array during view + initialization. You generally will not call this method directly unless + you are overriding `createChildViews()`. Note that this method will + automatically configure the correct settings on the new view instance to + act as a child of the parent. + + @method createChildView + @param {Class} viewClass + @param {Hash} [attrs] Attributes to add + @return {Ember.View} new instance + */ + createChildView: function(view, attrs) { + if (view.isView && view._parentView === this && view.container === this.container) { + return view; + } + + attrs = attrs || {}; + attrs._parentView = this; + attrs.container = this.container; + + if (Ember.CoreView.detect(view)) { + attrs.templateData = attrs.templateData || get(this, 'templateData'); + + view = view.create(attrs); + + // don't set the property on a virtual view, as they are invisible to + // consumers of the view API + if (view.viewName) { + set(get(this, 'concreteView'), view.viewName, view); + } + } else { + Ember.assert('You must pass instance or subclass of View', view.isView); + + Ember.setProperties(view, attrs); + + if (!get(view, 'templateData')) { + set(view, 'templateData', get(this, 'templateData')); + } + } + + return view; + }, + + becameVisible: Ember.K, + becameHidden: Ember.K, + + /** + @private + + When the view's `isVisible` property changes, toggle the visibility + element of the actual DOM element. + + @method _isVisibleDidChange + */ + _isVisibleDidChange: Ember.observer(function() { + var $el = this.$(); + if (!$el) { return; } + + var isVisible = get(this, 'isVisible'); + + $el.toggle(isVisible); + + if (this._isAncestorHidden()) { return; } + + if (isVisible) { + this._notifyBecameVisible(); + } else { + this._notifyBecameHidden(); + } + }, 'isVisible'), + + _notifyBecameVisible: function() { + this.trigger('becameVisible'); + + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); + + if (isVisible || isVisible === null) { + view._notifyBecameVisible(); + } + }); + }, + + _notifyBecameHidden: function() { + this.trigger('becameHidden'); + this.forEachChildView(function(view) { + var isVisible = get(view, 'isVisible'); + + if (isVisible || isVisible === null) { + view._notifyBecameHidden(); + } + }); + }, + + _isAncestorHidden: function() { + var parent = get(this, 'parentView'); + + while (parent) { + if (get(parent, 'isVisible') === false) { return true; } + + parent = get(parent, 'parentView'); + } + + return false; + }, + + clearBuffer: function() { + this.invokeRecursively(function(view) { + view.buffer = null; + }); + }, + + transitionTo: function(state, children) { + var priorState = this.currentState, + currentState = this.currentState = this.states[state]; + this.state = state; + + if (priorState && priorState.exit) { priorState.exit(this); } + if (currentState.enter) { currentState.enter(this); } + + if (children !== false) { + this.forEachChildView(function(view) { + view.transitionTo(state); + }); + } + }, + + // ....................................................... + // EVENT HANDLING + // + + /** + @private + + Handle events from `Ember.EventDispatcher` + + @method handleEvent + @param eventName {String} + @param evt {Event} + */ + handleEvent: function(eventName, evt) { + return this.currentState.handleEvent(this, eventName, evt); + }, + + registerObserver: function(root, path, target, observer) { + if (!observer && 'function' === typeof target) { + observer = target; + target = null; + } + + var view = this, + stateCheckedObserver = function(){ + view.currentState.invokeObserver(this, observer); + }, + scheduledObserver = function() { + Ember.run.scheduleOnce('render', this, stateCheckedObserver); + }; + + Ember.addObserver(root, path, target, scheduledObserver); + + this.one('willClearRender', function() { + Ember.removeObserver(root, path, target, scheduledObserver); + }); + } + +}); + +/* + Describe how the specified actions should behave in the various + states that a view can exist in. Possible states: + + * preRender: when a view is first instantiated, and after its + element was destroyed, it is in the preRender state + * inBuffer: once a view has been rendered, but before it has + been inserted into the DOM, it is in the inBuffer state + * inDOM: once a view has been inserted into the DOM it is in + the inDOM state. A view spends the vast majority of its + existence in this state. + * destroyed: once a view has been destroyed (using the destroy + method), it is in this state. No further actions can be invoked + on a destroyed view. +*/ + + // in the destroyed state, everything is illegal + + // before rendering has begun, all legal manipulations are noops. + + // inside the buffer, legal manipulations are done on the buffer + + // once the view has been inserted into the DOM, legal manipulations + // are done on the DOM element. + +function notifyMutationListeners() { + Ember.run.once(Ember.View, 'notifyMutationListeners'); +} + +var DOMManager = { + prepend: function(view, html) { + view.$().prepend(html); + notifyMutationListeners(); + }, + + after: function(view, html) { + view.$().after(html); + notifyMutationListeners(); + }, + + html: function(view, html) { + view.$().html(html); + notifyMutationListeners(); + }, + + replace: function(view) { + var element = get(view, 'element'); + + set(view, 'element', null); + + view._insertElementLater(function() { + Ember.$(element).replaceWith(get(view, 'element')); + notifyMutationListeners(); + }); + }, + + remove: function(view) { + view.$().remove(); + notifyMutationListeners(); + }, + + empty: function(view) { + view.$().empty(); + notifyMutationListeners(); + } +}; + +Ember.View.reopen({ + domManager: DOMManager +}); + +Ember.View.reopenClass({ + + /** + @private + + Parse a path and return an object which holds the parsed properties. + + For example a path like "content.isEnabled:enabled:disabled" wil return the + following object: + + ```javascript + { + path: "content.isEnabled", + className: "enabled", + falsyClassName: "disabled", + classNames: ":enabled:disabled" + } + ``` + + @method _parsePropertyPath + @static + */ + _parsePropertyPath: function(path) { + var split = path.split(':'), + propertyPath = split[0], + classNames = "", + className, + falsyClassName; + + // check if the property is defined as prop:class or prop:trueClass:falseClass + if (split.length > 1) { + className = split[1]; + if (split.length === 3) { falsyClassName = split[2]; } + + classNames = ':' + className; + if (falsyClassName) { classNames += ":" + falsyClassName; } + } + + return { + path: propertyPath, + classNames: classNames, + className: (className === '') ? undefined : className, + falsyClassName: falsyClassName + }; + }, + + /** + @private + + Get the class name for a given value, based on the path, optional + `className` and optional `falsyClassName`. + + - if a `className` or `falsyClassName` has been specified: + - if the value is truthy and `className` has been specified, + `className` is returned + - if the value is falsy and `falsyClassName` has been specified, + `falsyClassName` is returned + - otherwise `null` is returned + - if the value is `true`, the dasherized last part of the supplied path + is returned + - if the value is not `false`, `undefined` or `null`, the `value` + is returned + - if none of the above rules apply, `null` is returned + + @method _classStringForValue + @param path + @param val + @param className + @param falsyClassName + @static + */ + _classStringForValue: function(path, val, className, falsyClassName) { + // When using the colon syntax, evaluate the truthiness or falsiness + // of the value to determine which className to return + if (className || falsyClassName) { + if (className && !!val) { + return className; + + } else if (falsyClassName && !val) { + return falsyClassName; + + } else { + return null; + } + + // If value is a Boolean and true, return the dasherized property + // name. + } else if (val === true) { + // Normalize property path to be suitable for use + // as a class name. For exaple, content.foo.barBaz + // becomes bar-baz. + var parts = path.split('.'); + return Ember.String.dasherize(parts[parts.length-1]); + + // If the value is not false, undefined, or null, return the current + // value of the property. + } else if (val !== false && val != null) { + return val; + + // Nothing to display. Return null so that the old class is removed + // but no new class is added. + } else { + return null; + } + } +}); + +var mutation = Ember.Object.extend(Ember.Evented).create(); + +Ember.View.addMutationListener = function(callback) { + mutation.on('change', callback); +}; + +Ember.View.removeMutationListener = function(callback) { + mutation.off('change', callback); +}; + +Ember.View.notifyMutationListeners = function() { + mutation.trigger('change'); +}; + +/** + Global views hash + + @property views + @static + @type Hash +*/ +Ember.View.views = {}; + +// If someone overrides the child views computed property when +// defining their class, we want to be able to process the user's +// supplied childViews and then restore the original computed property +// at view initialization time. This happens in Ember.ContainerView's init +// method. +Ember.View.childViewsProperty = childViewsProperty; + +Ember.View.applyAttributeBindings = function(elem, name, value) { + var type = Ember.typeOf(value); + + // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js + if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) { + if (value !== elem.attr(name)) { + elem.attr(name, value); + } + } else if (name === 'value' || type === 'boolean') { + // We can't set properties to undefined or null + if (!value) { value = ''; } + + if (value !== elem.prop(name)) { + // value and booleans should always be properties + elem.prop(name, value); + } + } else if (!value) { + elem.removeAttr(name); + } +}; + +Ember.View.states = states; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; + +Ember.View.states._default = { + // appendChild is only legal while rendering the buffer. + appendChild: function() { + throw "You can't use appendChild outside of the rendering process"; + }, + + $: function() { + return undefined; + }, + + getElement: function() { + return null; + }, + + // Handle events from `Ember.EventDispatcher` + handleEvent: function() { + return true; // continue event propagation + }, + + destroyElement: function(view) { + set(view, 'element', null); + if (view._scheduledInsert) { + Ember.run.cancel(view._scheduledInsert); + view._scheduledInsert = null; + } + return view; + }, + + renderToBufferIfNeeded: function () { + return false; + }, + + rerender: Ember.K, + invokeObserver: Ember.K +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var preRender = Ember.View.states.preRender = Ember.create(Ember.View.states._default); + +Ember.merge(preRender, { + // a view leaves the preRender state once its element has been + // created (createElement). + insertElement: function(view, fn) { + view.createElement(); + var viewCollection = view.viewHierarchyCollection(); + + viewCollection.trigger('willInsertElement'); + // after createElement, the view will be in the hasElement state. + fn.call(view); + viewCollection.transitionTo('inDOM', false); + viewCollection.trigger('didInsertElement'); + }, + + renderToBufferIfNeeded: function(view, buffer) { + view.renderToBuffer(buffer); + return true; + }, + + empty: Ember.K, + + setElement: function(view, value) { + if (value !== null) { + view.transitionTo('hasElement'); + } + return value; + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; + +var inBuffer = Ember.View.states.inBuffer = Ember.create(Ember.View.states._default); + +Ember.merge(inBuffer, { + $: function(view, sel) { + // if we don't have an element yet, someone calling this.$() is + // trying to update an element that isn't in the DOM. Instead, + // rerender the view to allow the render method to reflect the + // changes. + view.rerender(); + return Ember.$(); + }, + + // when a view is rendered in a buffer, rerendering it simply + // replaces the existing buffer with a new one + rerender: function(view) { + throw new Ember.Error("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM."); + }, + + // when a view is rendered in a buffer, appending a child + // view will render that view and append the resulting + // buffer into its buffer. + appendChild: function(view, childView, options) { + var buffer = view.buffer, _childViews = view._childViews; + + childView = view.createChildView(childView, options); + if (!_childViews.length) { _childViews = view._childViews = _childViews.slice(); } + _childViews.push(childView); + + childView.renderToBuffer(buffer); + + view.propertyDidChange('childViews'); + + return childView; + }, + + // when a view is rendered in a buffer, destroying the + // element will simply destroy the buffer and put the + // state back into the preRender state. + destroyElement: function(view) { + view.clearBuffer(); + var viewCollection = view._notifyWillDestroyElement(); + viewCollection.transitionTo('preRender', false); + + return view; + }, + + empty: function() { + Ember.assert("Emptying a view in the inBuffer state is not allowed and should not happen under normal circumstances. Most likely there is a bug in your application. This may be due to excessive property change notifications."); + }, + + renderToBufferIfNeeded: function (view, buffer) { + return false; + }, + + // It should be impossible for a rendered view to be scheduled for + // insertion. + insertElement: function() { + throw "You can't insert an element that has already been rendered"; + }, + + setElement: function(view, value) { + if (value === null) { + view.transitionTo('preRender'); + } else { + view.clearBuffer(); + view.transitionTo('hasElement'); + } + + return value; + }, + + invokeObserver: function(target, observer) { + observer.call(target); + } +}); + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; + +var hasElement = Ember.View.states.hasElement = Ember.create(Ember.View.states._default); + +Ember.merge(hasElement, { + $: function(view, sel) { + var elem = get(view, 'element'); + return sel ? Ember.$(sel, elem) : Ember.$(elem); + }, + + getElement: function(view) { + var parent = get(view, 'parentView'); + if (parent) { parent = get(parent, 'element'); } + if (parent) { return view.findElementInParentElement(parent); } + return Ember.$("#" + get(view, 'elementId'))[0]; + }, + + setElement: function(view, value) { + if (value === null) { + view.transitionTo('preRender'); + } else { + throw "You cannot set an element to a non-null value when the element is already in the DOM."; + } + + return value; + }, + + // once the view has been inserted into the DOM, rerendering is + // deferred to allow bindings to synchronize. + rerender: function(view) { + view.triggerRecursively('willClearRender'); + + view.clearRenderedChildren(); + + view.domManager.replace(view); + return view; + }, + + // once the view is already in the DOM, destroying it removes it + // from the DOM, nukes its element, and puts it back into the + // preRender state if inDOM. + + destroyElement: function(view) { + view._notifyWillDestroyElement(); + view.domManager.remove(view); + set(view, 'element', null); + if (view._scheduledInsert) { + Ember.run.cancel(view._scheduledInsert); + view._scheduledInsert = null; + } + return view; + }, + + empty: function(view) { + var _childViews = view._childViews, len, idx; + if (_childViews) { + len = _childViews.length; + for (idx = 0; idx < len; idx++) { + _childViews[idx]._notifyWillDestroyElement(); + } + } + view.domManager.empty(view); + }, + + // Handle events from `Ember.EventDispatcher` + handleEvent: function(view, eventName, evt) { + if (view.has(eventName)) { + // Handler should be able to re-dispatch events, so we don't + // preventDefault or stopPropagation. + return view.trigger(eventName, evt); + } else { + return true; // continue event propagation + } + }, + + invokeObserver: function(target, observer) { + observer.call(target); + } +}); + +var inDOM = Ember.View.states.inDOM = Ember.create(hasElement); + +Ember.merge(inDOM, { + enter: function(view) { + // Register the view for event handling. This hash is used by + // Ember.EventDispatcher to dispatch incoming events. + if (!view.isVirtual) { + Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !Ember.View.views[view.elementId]); + Ember.View.views[view.elementId] = view; + } + + view.addBeforeObserver('elementId', function() { + throw new Error("Changing a view's elementId after creation is not allowed"); + }); + }, + + exit: function(view) { + if (!this.isVirtual) delete Ember.View.views[view.elementId]; + }, + + insertElement: function(view, fn) { + throw "You can't insert an element into the DOM that has already been inserted"; + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var destroyingError = "You can't call %@ on a view being destroyed", fmt = Ember.String.fmt; + +var destroying = Ember.View.states.destroying = Ember.create(Ember.View.states._default); + +Ember.merge(destroying, { + appendChild: function() { + throw fmt(destroyingError, ['appendChild']); + }, + rerender: function() { + throw fmt(destroyingError, ['rerender']); + }, + destroyElement: function() { + throw fmt(destroyingError, ['destroyElement']); + }, + empty: function() { + throw fmt(destroyingError, ['empty']); + }, + + setElement: function() { + throw fmt(destroyingError, ["set('element', ...)"]); + }, + + renderToBufferIfNeeded: function() { + return false; + }, + + // Since element insertion is scheduled, don't do anything if + // the view has been destroyed between scheduling and execution + insertElement: Ember.K +}); + + +})(); + + + +(function() { +Ember.View.cloneStates = function(from) { + var into = {}; + + into._default = {}; + into.preRender = Ember.create(into._default); + into.destroying = Ember.create(into._default); + into.inBuffer = Ember.create(into._default); + into.hasElement = Ember.create(into._default); + into.inDOM = Ember.create(into.hasElement); + + for (var stateName in from) { + if (!from.hasOwnProperty(stateName)) { continue; } + Ember.merge(into[stateName], from[stateName]); + } + + return into; +}; + +})(); + + + +(function() { +var states = Ember.View.cloneStates(Ember.View.states); + +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set; +var forEach = Ember.EnumerableUtils.forEach; +var ViewCollection = Ember._ViewCollection; + +/** + A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` + allowing programatic management of its child views. + + ## Setting Initial Child Views + + The initial array of child views can be set in one of two ways. You can + provide a `childViews` property at creation time that contains instance of + `Ember.View`: + + ```javascript + aContainer = Ember.ContainerView.create({ + childViews: [Ember.View.create(), Ember.View.create()] + }); + ``` + + You can also provide a list of property names whose values are instances of + `Ember.View`: + + ```javascript + aContainer = Ember.ContainerView.create({ + childViews: ['aView', 'bView', 'cView'], + aView: Ember.View.create(), + bView: Ember.View.create(), + cView: Ember.View.create() + }); + ``` + + The two strategies can be combined: + + ```javascript + aContainer = Ember.ContainerView.create({ + childViews: ['aView', Ember.View.create()], + aView: Ember.View.create() + }); + ``` + + Each child view's rendering will be inserted into the container's rendered + HTML in the same order as its position in the `childViews` property. + + ## Adding and Removing Child Views + + The container view implements `Ember.MutableArray` allowing programatic management of its child views. + + To remove a view, pass that view into a `removeObject` call on the container view. + + Given an empty `<body>` the following code + + ```javascript + aContainer = Ember.ContainerView.create({ + classNames: ['the-container'], + childViews: ['aView', 'bView'], + aView: Ember.View.create({ + template: Ember.Handlebars.compile("A") + }), + bView: Ember.View.create({ + template: Ember.Handlebars.compile("B") + }) + }); + + aContainer.appendTo('body'); + ``` + + Results in the HTML + + ```html + <div class="ember-view the-container"> + <div class="ember-view">A</div> + <div class="ember-view">B</div> + </div> + ``` + + Removing a view + + ```javascript + aContainer.toArray(); // [aContainer.aView, aContainer.bView] + aContainer.removeObject(aContainer.get('bView')); + aContainer.toArray(); // [aContainer.aView] + ``` + + Will result in the following HTML + + ```html + <div class="ember-view the-container"> + <div class="ember-view">A</div> + </div> + ``` + + Similarly, adding a child view is accomplished by adding `Ember.View` instances to the + container view. + + Given an empty `<body>` the following code + + ```javascript + aContainer = Ember.ContainerView.create({ + classNames: ['the-container'], + childViews: ['aView', 'bView'], + aView: Ember.View.create({ + template: Ember.Handlebars.compile("A") + }), + bView: Ember.View.create({ + template: Ember.Handlebars.compile("B") + }) + }); + + aContainer.appendTo('body'); + ``` + + Results in the HTML + + ```html + <div class="ember-view the-container"> + <div class="ember-view">A</div> + <div class="ember-view">B</div> + </div> + ``` + + Adding a view + + ```javascript + AnotherViewClass = Ember.View.extend({ + template: Ember.Handlebars.compile("Another view") + }); + + aContainer.toArray(); // [aContainer.aView, aContainer.bView] + aContainer.pushObject(AnotherViewClass.create()); + aContainer.toArray(); // [aContainer.aView, aContainer.bView, <AnotherViewClass instance>] + ``` + + Will result in the following HTML + + ```html + <div class="ember-view the-container"> + <div class="ember-view">A</div> + <div class="ember-view">B</div> + <div class="ember-view">Another view</div> + </div> + ``` + + ## Templates and Layout + + A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or + `defaultLayout` property on a container view will not result in the template + or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM + representation will only be the rendered HTML of its child views. + + ## Binding a View to Display + + If you would like to display a single view in your ContainerView, you can set + its `currentView` property. When the `currentView` property is set to a view + instance, it will be added to the ContainerView. If the `currentView` property + is later changed to a different view, the new view will replace the old view. + If `currentView` is set to `null`, the last `currentView` will be removed. + + This functionality is useful for cases where you want to bind the display of + a ContainerView to a controller or state manager. For example, you can bind + the `currentView` of a container to a controller like this: + + ```javascript + App.appController = Ember.Object.create({ + view: Ember.View.create({ + templateName: 'person_template' + }) + }); + ``` + + ```handlebars + {{view Ember.ContainerView currentViewBinding="App.appController.view"}} + ``` + + @class ContainerView + @namespace Ember + @extends Ember.View +*/ +Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { + states: states, + + init: function() { + this._super(); + + var childViews = get(this, 'childViews'); + + // redefine view's childViews property that was obliterated + Ember.defineProperty(this, 'childViews', Ember.View.childViewsProperty); + + var _childViews = this._childViews; + + forEach(childViews, function(viewName, idx) { + var view; + + if ('string' === typeof viewName) { + view = get(this, viewName); + view = this.createChildView(view); + set(this, viewName, view); + } else { + view = this.createChildView(viewName); + } + + _childViews[idx] = view; + }, this); + + var currentView = get(this, 'currentView'); + if (currentView) { + if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } + _childViews.push(this.createChildView(currentView)); + } + }, + + replace: function(idx, removedCount, addedViews) { + var addedCount = addedViews ? get(addedViews, 'length') : 0; + var self = this; + Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); + + this.arrayContentWillChange(idx, removedCount, addedCount); + this.childViewsWillChange(this._childViews, idx, removedCount); + + if (addedCount === 0) { + this._childViews.splice(idx, removedCount) ; + } else { + var args = [idx, removedCount].concat(addedViews); + if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); } + this._childViews.splice.apply(this._childViews, args); + } + + this.arrayContentDidChange(idx, removedCount, addedCount); + this.childViewsDidChange(this._childViews, idx, removedCount, addedCount); + + return this; + }, + + objectAt: function(idx) { + return this._childViews[idx]; + }, + + length: Ember.computed(function () { + return this._childViews.length; + }), + + /** + @private + + Instructs each child view to render to the passed render buffer. + + @method render + @param {Ember.RenderBuffer} buffer the buffer to render to + */ + render: function(buffer) { + this.forEachChildView(function(view) { + view.renderToBuffer(buffer); + }); + }, + + instrumentName: 'container', + + /** + @private + + When a child view is removed, destroy its element so that + it is removed from the DOM. + + The array observer that triggers this action is set up in the + `renderToBuffer` method. + + @method childViewsWillChange + @param {Ember.Array} views the child views array before mutation + @param {Number} start the start position of the mutation + @param {Number} removed the number of child views removed + **/ + childViewsWillChange: function(views, start, removed) { + this.propertyWillChange('childViews'); + + if (removed > 0) { + var changedViews = views.slice(start, start+removed); + // transition to preRender before clearing parentView + this.currentState.childViewsWillChange(this, views, start, removed); + this.initializeViews(changedViews, null, null); + } + }, + + removeChild: function(child) { + this.removeObject(child); + return this; + }, + + /** + @private + + When a child view is added, make sure the DOM gets updated appropriately. + + If the view has already rendered an element, we tell the child view to + create an element and insert it into the DOM. If the enclosing container + view has already written to a buffer, but not yet converted that buffer + into an element, we insert the string representation of the child into the + appropriate place in the buffer. + + @method childViewsDidChange + @param {Ember.Array} views the array of child views afte the mutation has occurred + @param {Number} start the start position of the mutation + @param {Number} removed the number of child views removed + @param {Number} the number of child views added + */ + childViewsDidChange: function(views, start, removed, added) { + if (added > 0) { + var changedViews = views.slice(start, start+added); + this.initializeViews(changedViews, this, get(this, 'templateData')); + this.currentState.childViewsDidChange(this, views, start, added); + } + this.propertyDidChange('childViews'); + }, + + initializeViews: function(views, parentView, templateData) { + forEach(views, function(view) { + set(view, '_parentView', parentView); + set(view, 'container', parentView && parentView.container); + + if (!get(view, 'templateData')) { + set(view, 'templateData', templateData); + } + }); + }, + + currentView: null, + + _currentViewWillChange: Ember.beforeObserver(function() { + var currentView = get(this, 'currentView'); + if (currentView) { + currentView.destroy(); + } + }, 'currentView'), + + _currentViewDidChange: Ember.observer(function() { + var currentView = get(this, 'currentView'); + if (currentView) { + Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); + this.pushObject(currentView); + } + }, 'currentView'), + + _ensureChildrenAreInDOM: function () { + this.currentState.ensureChildrenAreInDOM(this); + } +}); + +Ember.merge(states._default, { + childViewsWillChange: Ember.K, + childViewsDidChange: Ember.K, + ensureChildrenAreInDOM: Ember.K +}); + +Ember.merge(states.inBuffer, { + childViewsDidChange: function(parentView, views, start, added) { + throw new Error('You cannot modify child views while in the inBuffer state'); + } +}); + +Ember.merge(states.hasElement, { + childViewsWillChange: function(view, views, start, removed) { + for (var i=start; i<start+removed; i++) { + views[i].remove(); + } + }, + + childViewsDidChange: function(view, views, start, added) { + Ember.run.scheduleOnce('render', view, '_ensureChildrenAreInDOM'); + }, + + ensureChildrenAreInDOM: function(view) { + var childViews = view._childViews, i, len, childView, previous, buffer, viewCollection = new ViewCollection(); + + for (i = 0, len = childViews.length; i < len; i++) { + childView = childViews[i]; + + if (!buffer) { buffer = Ember.RenderBuffer(); buffer._hasElement = false; } + + if (childView.renderToBufferIfNeeded(buffer)) { + viewCollection.push(childView); + } else if (viewCollection.length) { + insertViewCollection(view, viewCollection, previous, buffer); + buffer = null; + previous = childView; + viewCollection.clear(); + } else { + previous = childView; + } + } + + if (viewCollection.length) { + insertViewCollection(view, viewCollection, previous, buffer); + } + } +}); + +function insertViewCollection(view, viewCollection, previous, buffer) { + viewCollection.triggerRecursively('willInsertElement'); + + if (previous) { + previous.domManager.after(previous, buffer.string()); + } else { + view.domManager.prepend(view, buffer.string()); + } + + viewCollection.forEach(function(v) { + v.transitionTo('inDOM'); + v.propertyDidChange('element'); + v.triggerRecursively('didInsertElement'); + }); +} + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; + +/** + `Ember.CollectionView` is an `Ember.View` descendent responsible for managing + a collection (an array or array-like object) by maintaining a child view object + and associated DOM representation for each item in the array and ensuring + that child views and their associated rendered HTML are updated when items in + the array are added, removed, or replaced. + + ## Setting content + + The managed collection of objects is referenced as the `Ember.CollectionView` + instance's `content` property. + + ```javascript + someItemsView = Ember.CollectionView.create({ + content: ['A', 'B','C'] + }) + ``` + + The view for each item in the collection will have its `content` property set + to the item. + + ## Specifying itemViewClass + + By default the view class for each item in the managed collection will be an + instance of `Ember.View`. You can supply a different class by setting the + `CollectionView`'s `itemViewClass` property. + + Given an empty `<body>` and the following code: + + ```javascript + someItemsView = Ember.CollectionView.create({ + classNames: ['a-collection'], + content: ['A','B','C'], + itemViewClass: Ember.View.extend({ + template: Ember.Handlebars.compile("the letter: {{view.content}}") + }) + }); + + someItemsView.appendTo('body'); + ``` + + Will result in the following HTML structure + + ```html + <div class="ember-view a-collection"> + <div class="ember-view">the letter: A</div> + <div class="ember-view">the letter: B</div> + <div class="ember-view">the letter: C</div> + </div> + ``` + + ## Automatic matching of parent/child tagNames + + Setting the `tagName` property of a `CollectionView` to any of + "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result + in the item views receiving an appropriately matched `tagName` property. + + Given an empty `<body>` and the following code: + + ```javascript + anUndorderedListView = Ember.CollectionView.create({ + tagName: 'ul', + content: ['A','B','C'], + itemViewClass: Ember.View.extend({ + template: Ember.Handlebars.compile("the letter: {{view.content}}") + }) + }); + + anUndorderedListView.appendTo('body'); + ``` + + Will result in the following HTML structure + + ```html + <ul class="ember-view a-collection"> + <li class="ember-view">the letter: A</li> + <li class="ember-view">the letter: B</li> + <li class="ember-view">the letter: C</li> + </ul> + ``` + + Additional `tagName` pairs can be provided by adding to + `Ember.CollectionView.CONTAINER_MAP ` + + ```javascript + Ember.CollectionView.CONTAINER_MAP['article'] = 'section' + ``` + + ## Programatic creation of child views + + For cases where additional customization beyond the use of a single + `itemViewClass` or `tagName` matching is required CollectionView's + `createChildView` method can be overidden: + + ```javascript + CustomCollectionView = Ember.CollectionView.extend({ + createChildView: function(viewClass, attrs) { + if (attrs.content.kind == 'album') { + viewClass = App.AlbumView; + } else { + viewClass = App.SongView; + } + return this._super(viewClass, attrs); + } + }); + ``` + + ## Empty View + + You can provide an `Ember.View` subclass to the `Ember.CollectionView` + instance as its `emptyView` property. If the `content` property of a + `CollectionView` is set to `null` or an empty array, an instance of this view + will be the `CollectionView`s only child. + + ```javascript + aListWithNothing = Ember.CollectionView.create({ + classNames: ['nothing'] + content: null, + emptyView: Ember.View.extend({ + template: Ember.Handlebars.compile("The collection is empty") + }) + }); + + aListWithNothing.appendTo('body'); + ``` + + Will result in the following HTML structure + + ```html + <div class="ember-view nothing"> + <div class="ember-view"> + The collection is empty + </div> + </div> + ``` + + ## Adding and Removing items + + The `childViews` property of a `CollectionView` should not be directly + manipulated. Instead, add, remove, replace items from its `content` property. + This will trigger appropriate changes to its rendered HTML. + + ## Use in templates via the `{{collection}}` `Ember.Handlebars` helper + + `Ember.Handlebars` provides a helper specifically for adding + `CollectionView`s to templates. See `Ember.Handlebars.collection` for more + details + + @class CollectionView + @namespace Ember + @extends Ember.ContainerView + @since Ember 0.9 +*/ +Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ { + + /** + A list of items to be displayed by the `Ember.CollectionView`. + + @property content + @type Ember.Array + @default null + */ + content: null, + + /** + @private + + This provides metadata about what kind of empty view class this + collection would like if it is being instantiated from another + system (like Handlebars) + + @property emptyViewClass + */ + emptyViewClass: Ember.View, + + /** + An optional view to display if content is set to an empty array. + + @property emptyView + @type Ember.View + @default null + */ + emptyView: null, + + /** + @property itemViewClass + @type Ember.View + @default Ember.View + */ + itemViewClass: Ember.View, + + init: function() { + var ret = this._super(); + this._contentDidChange(); + return ret; + }, + + _contentWillChange: Ember.beforeObserver(function() { + var content = this.get('content'); + + if (content) { content.removeArrayObserver(this); } + var len = content ? get(content, 'length') : 0; + this.arrayWillChange(content, 0, len); + }, 'content'), + + /** + @private + + Check to make sure that the content has changed, and if so, + update the children directly. This is always scheduled + asynchronously, to allow the element to be created before + bindings have synchronized and vice versa. + + @method _contentDidChange + */ + _contentDidChange: Ember.observer(function() { + var content = get(this, 'content'); + + if (content) { + Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), Ember.Array.detect(content)); + content.addArrayObserver(this); + } + + var len = content ? get(content, 'length') : 0; + this.arrayDidChange(content, 0, null, len); + }, 'content'), + + destroy: function() { + if (!this._super()) { return; } + + var content = get(this, 'content'); + if (content) { content.removeArrayObserver(this); } + + if (this._createdEmptyView) { + this._createdEmptyView.destroy(); + } + + return this; + }, + + arrayWillChange: function(content, start, removedCount) { + // If the contents were empty before and this template collection has an + // empty view remove it now. + var emptyView = get(this, 'emptyView'); + if (emptyView && emptyView instanceof Ember.View) { + emptyView.removeFromParent(); + } + + // Loop through child views that correspond with the removed items. + // Note that we loop from the end of the array to the beginning because + // we are mutating it as we go. + var childViews = this._childViews, childView, idx, len; + + len = this._childViews.length; + + var removingAll = removedCount === len; + + if (removingAll) { + this.currentState.empty(this); + this.invokeRecursively(function(view) { + view.removedFromDOM = true; + }, false); + } + + for (idx = start + removedCount - 1; idx >= start; idx--) { + childView = childViews[idx]; + childView.destroy(); + } + }, + + /** + Called when a mutation to the underlying content array occurs. + + This method will replay that mutation against the views that compose the + `Ember.CollectionView`, ensuring that the view reflects the model. + + This array observer is added in `contentDidChange`. + + @method arrayDidChange + @param {Array} content the managed collection of objects + @param {Number} start the index at which the changes occurred + @param {Number} removed number of object removed from content + @param {Number} added number of object added to content + */ + arrayDidChange: function(content, start, removed, added) { + var itemViewClass = get(this, 'itemViewClass'), + addedViews = [], view, item, idx, len; + + if ('string' === typeof itemViewClass) { + itemViewClass = get(itemViewClass); + } + + Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", [itemViewClass]), Ember.View.detect(itemViewClass)); + + len = content ? get(content, 'length') : 0; + if (len) { + for (idx = start; idx < start+added; idx++) { + item = content.objectAt(idx); + + view = this.createChildView(itemViewClass, { + content: item, + contentIndex: idx + }); + + addedViews.push(view); + } + } else { + var emptyView = get(this, 'emptyView'); + if (!emptyView) { return; } + + var isClass = Ember.CoreView.detect(emptyView); + + emptyView = this.createChildView(emptyView); + addedViews.push(emptyView); + set(this, 'emptyView', emptyView); + + if (isClass) { this._createdEmptyView = emptyView; } + } + this.replace(start, 0, addedViews); + }, + + createChildView: function(view, attrs) { + view = this._super(view, attrs); + + var itemTagName = get(view, 'tagName'); + var tagName = (itemTagName === null || itemTagName === undefined) ? Ember.CollectionView.CONTAINER_MAP[get(this, 'tagName')] : itemTagName; + + set(view, 'tagName', tagName); + + return view; + } +}); + +/** + A map of parent tags to their default child tags. You can add + additional parent tags if you want collection views that use + a particular parent tag to default to a child tag. + + @property CONTAINER_MAP + @type Hash + @static + @final +*/ +Ember.CollectionView.CONTAINER_MAP = { + ul: 'li', + ol: 'li', + table: 'tr', + thead: 'tr', + tbody: 'tr', + tfoot: 'tr', + tr: 'td', + select: 'option' +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +/** + An `Ember.Component` is a view that is completely + isolated. Property access in its templates go + to the view object and actions are targeted at + the view object. There is no access to the + surrounding context or outer controller; all + contextual information is passed in. + + The easiest way to create an `Ember.Component` is via + a template. If you name a template + `controls/my-foo`, you will be able to use + `{{my-foo}}` in other templates, which will make + an instance of the isolated control. + + ```html + {{app-profile person=currentUser}} + ``` + + ```html + <!-- app-profile template --> + <h1>{{person.title}}</h1> + <img {{bindAttr src=person.avatar}}> + <p class='signature'>{{person.signature}}</p> + ``` + + You can also use `yield` inside a template to + include the **contents** of the custom tag: + + ```html + {{#my-profile person=currentUser}} + <p>Admin mode</p> + {{/my-profile}} + ``` + + ```html + <!-- app-profile template --> + + <h1>{{person.title}}</h1> + {{yield}} <!-- block contents --> + ``` + + If you want to customize the control, in order to + handle events or actions, you implement a subclass + of `Ember.Component` named after the name of the + control. + + For example, you could implement the action + `hello` for the `app-profile` control: + + ```js + App.AppProfileComponent = Ember.Component.extend({ + hello: function(name) { + console.log("Hello", name) + } + }); + ``` + + And then use it in the control's template: + + ```html + <!-- app-profile template --> + + <h1>{{person.title}}</h1> + {{yield}} <!-- block contents --> + + <button {{action 'hello' person.name}}> + Say Hello to {{person.name}} + </button> + ``` + + Components must have a `-` in their name to avoid + conflicts with built-in controls that wrap HTML + elements. This is consistent with the same + requirement in web components. + + @class Component + @namespace Ember + @extends Ember.View +*/ +Ember.Component = Ember.View.extend({ + init: function() { + this._super(); + this.set('context', this); + this.set('controller', this); + } +}); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +`Ember.ViewTargetActionSupport` is a mixin that can be included in a +view class to add a `triggerAction` method with semantics similar to +the Handlebars `{{action}}` helper. It provides intelligent defaults +for the action's target: the view's controller; and the context that is +sent with the action: the view's context. + +Note: In normal Ember usage, the `{{action}}` helper is usually the best +choice. This mixin is most often useful when you are doing more complex +event handling in custom View subclasses. + +For example: + +```javascript +App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { + action: 'save', + click: function(){ + this.triggerAction(); // Sends the `save` action, along with the current context + // to the current controller + } +}); +``` + +The `action` can be provided as properties of an optional object argument +to `triggerAction` as well. + +```javascript +App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { + click: function(){ + this.triggerAction({ + action: 'save' + }); // Sends the `save` action, along with the current context + // to the current controller + } +}); +``` + +@class ViewTargetActionSupport +@namespace Ember +@extends Ember.TargetActionSupport +*/ +Ember.ViewTargetActionSupport = Ember.Mixin.create(Ember.TargetActionSupport, { + /** + @property target + */ + target: Ember.computed.alias('controller'), + /** + @property actionContext + */ + actionContext: Ember.computed.alias('context') +}); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +Ember Views + +@module ember +@submodule ember-views +@requires ember-runtime +@main ember-views +*/ + +})(); + +(function() { +define("metamorph", + [], + function() { + "use strict"; + // ========================================================================== + // Project: metamorph + // Copyright: ©2011 My Company Inc. All rights reserved. + // ========================================================================== + + var K = function(){}, + guid = 0, + document = this.document, + + // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges + supportsRange = document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, + + // Internet Explorer prior to 9 does not allow setting innerHTML if the first element + // is a "zero-scope" element. This problem can be worked around by making + // the first node an invisible text node. We, like Modernizr, use ­ + needsShy = document && (function(){ + var testEl = document.createElement('div'); + testEl.innerHTML = "<div></div>"; + testEl.firstChild.innerHTML = "<script></script>"; + return testEl.firstChild.innerHTML === ''; + })(), + + + // IE 8 (and likely earlier) likes to move whitespace preceeding + // a script tag to appear after it. This means that we can + // accidentally remove whitespace when updating a morph. + movesWhitespace = document && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "Test: <script type='text/x-placeholder'></script>Value"; + return testEl.childNodes[0].nodeValue === 'Test:' && + testEl.childNodes[2].nodeValue === ' Value'; + })(); + + // Constructor that supports either Metamorph('foo') or new + // Metamorph('foo'); + // + // Takes a string of HTML as the argument. + + var Metamorph = function(html) { + var self; + + if (this instanceof Metamorph) { + self = this; + } else { + self = new K(); + } + + self.innerHTML = html; + var myGuid = 'metamorph-'+(guid++); + self.start = myGuid + '-start'; + self.end = myGuid + '-end'; + + return self; + }; + + K.prototype = Metamorph.prototype; + + var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; + + outerHTMLFunc = function() { + return this.startTag() + this.innerHTML + this.endTag(); + }; + + startTagFunc = function() { + /* + * We replace chevron by its hex code in order to prevent escaping problems. + * Check this thread for more explaination: + * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript + */ + return "<script id='" + this.start + "' type='text/x-placeholder'>\x3C/script>"; + }; + + endTagFunc = function() { + /* + * We replace chevron by its hex code in order to prevent escaping problems. + * Check this thread for more explaination: + * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript + */ + return "<script id='" + this.end + "' type='text/x-placeholder'>\x3C/script>"; + }; + + // If we have the W3C range API, this process is relatively straight forward. + if (supportsRange) { + + // Get a range for the current morph. Optionally include the starting and + // ending placeholders. + rangeFor = function(morph, outerToo) { + var range = document.createRange(); + var before = document.getElementById(morph.start); + var after = document.getElementById(morph.end); + + if (outerToo) { + range.setStartBefore(before); + range.setEndAfter(after); + } else { + range.setStartAfter(before); + range.setEndBefore(after); + } + + return range; + }; + + htmlFunc = function(html, outerToo) { + // get a range for the current metamorph object + var range = rangeFor(this, outerToo); + + // delete the contents of the range, which will be the + // nodes between the starting and ending placeholder. + range.deleteContents(); + + // create a new document fragment for the HTML + var fragment = range.createContextualFragment(html); + + // insert the fragment into the range + range.insertNode(fragment); + }; + + removeFunc = function() { + // get a range for the current metamorph object including + // the starting and ending placeholders. + var range = rangeFor(this, true); + + // delete the entire range. + range.deleteContents(); + }; + + appendToFunc = function(node) { + var range = document.createRange(); + range.setStart(node); + range.collapse(false); + var frag = range.createContextualFragment(this.outerHTML()); + node.appendChild(frag); + }; + + afterFunc = function(html) { + var range = document.createRange(); + var after = document.getElementById(this.end); + + range.setStartAfter(after); + range.setEndAfter(after); + + var fragment = range.createContextualFragment(html); + range.insertNode(fragment); + }; + + prependFunc = function(html) { + var range = document.createRange(); + var start = document.getElementById(this.start); + + range.setStartAfter(start); + range.setEndAfter(start); + + var fragment = range.createContextualFragment(html); + range.insertNode(fragment); + }; + + } else { + /** + * This code is mostly taken from jQuery, with one exception. In jQuery's case, we + * have some HTML and we need to figure out how to convert it into some nodes. + * + * In this case, jQuery needs to scan the HTML looking for an opening tag and use + * that as the key for the wrap map. In our case, we know the parent node, and + * can use its type as the key for the wrap map. + **/ + var wrapMap = { + select: [ 1, "<select multiple='multiple'>", "</select>" ], + fieldset: [ 1, "<fieldset>", "</fieldset>" ], + table: [ 1, "<table>", "</table>" ], + tbody: [ 2, "<table><tbody>", "</tbody></table>" ], + tr: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + colgroup: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], + map: [ 1, "<map>", "</map>" ], + _default: [ 0, "", "" ] + }; + + var findChildById = function(element, id) { + if (element.getAttribute('id') === id) { return element; } + + var len = element.childNodes.length, idx, node, found; + for (idx=0; idx<len; idx++) { + node = element.childNodes[idx]; + found = node.nodeType === 1 && findChildById(node, id); + if (found) { return found; } + } + }; + + var setInnerHTML = function(element, html) { + var matches = []; + if (movesWhitespace) { + // Right now we only check for script tags with ids with the + // goal of targeting morphs. + html = html.replace(/(\s+)(<script id='([^']+)')/g, function(match, spaces, tag, id) { + matches.push([id, spaces]); + return tag; + }); + } + + element.innerHTML = html; + + // If we have to do any whitespace adjustments do them now + if (matches.length > 0) { + var len = matches.length, idx; + for (idx=0; idx<len; idx++) { + var script = findChildById(element, matches[idx][0]), + node = document.createTextNode(matches[idx][1]); + script.parentNode.insertBefore(node, script); + } + } + }; + + /** + * Given a parent node and some HTML, generate a set of nodes. Return the first + * node, which will allow us to traverse the rest using nextSibling. + * + * We need to do this because innerHTML in IE does not really parse the nodes. + **/ + var firstNodeFor = function(parentNode, html) { + var arr = wrapMap[parentNode.tagName.toLowerCase()] || wrapMap._default; + var depth = arr[0], start = arr[1], end = arr[2]; + + if (needsShy) { html = '­'+html; } + + var element = document.createElement('div'); + + setInnerHTML(element, start + html + end); + + for (var i=0; i<=depth; i++) { + element = element.firstChild; + } + + // Look for ­ to remove it. + if (needsShy) { + var shyElement = element; + + // Sometimes we get nameless elements with the shy inside + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + + // At this point it's the actual unicode character. + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } + } + + return element; + }; + + /** + * In some cases, Internet Explorer can create an anonymous node in + * the hierarchy with no tagName. You can create this scenario via: + * + * div = document.createElement("div"); + * div.innerHTML = "<table>­<script></script><tr><td>hi</td></tr></table>"; + * div.firstChild.firstChild.tagName //=> "" + * + * If our script markers are inside such a node, we need to find that + * node and use *it* as the marker. + **/ + var realNode = function(start) { + while (start.parentNode.tagName === "") { + start = start.parentNode; + } + + return start; + }; + + /** + * When automatically adding a tbody, Internet Explorer inserts the + * tbody immediately before the first <tr>. Other browsers create it + * before the first node, no matter what. + * + * This means the the following code: + * + * div = document.createElement("div"); + * div.innerHTML = "<table><script id='first'></script><tr><td>hi</td></tr><script id='last'></script></table> + * + * Generates the following DOM in IE: + * + * + div + * + table + * - script id='first' + * + tbody + * + tr + * + td + * - "hi" + * - script id='last' + * + * Which means that the two script tags, even though they were + * inserted at the same point in the hierarchy in the original + * HTML, now have different parents. + * + * This code reparents the first script tag by making it the tbody's + * first child. + **/ + var fixParentage = function(start, end) { + if (start.parentNode !== end.parentNode) { + end.parentNode.insertBefore(start, end.parentNode.firstChild); + } + }; + + htmlFunc = function(html, outerToo) { + // get the real starting node. see realNode for details. + var start = realNode(document.getElementById(this.start)); + var end = document.getElementById(this.end); + var parentNode = end.parentNode; + var node, nextSibling, last; + + // make sure that the start and end nodes share the same + // parent. If not, fix it. + fixParentage(start, end); + + // remove all of the nodes after the starting placeholder and + // before the ending placeholder. + node = start.nextSibling; + while (node) { + nextSibling = node.nextSibling; + last = node === end; + + // if this is the last node, and we want to remove it as well, + // set the `end` node to the next sibling. This is because + // for the rest of the function, we insert the new nodes + // before the end (note that insertBefore(node, null) is + // the same as appendChild(node)). + // + // if we do not want to remove it, just break. + if (last) { + if (outerToo) { end = node.nextSibling; } else { break; } + } + + node.parentNode.removeChild(node); + + // if this is the last node and we didn't break before + // (because we wanted to remove the outer nodes), break + // now. + if (last) { break; } + + node = nextSibling; + } + + // get the first node for the HTML string, even in cases like + // tables and lists where a simple innerHTML on a div would + // swallow some of the content. + node = firstNodeFor(start.parentNode, html); + + // copy the nodes for the HTML between the starting and ending + // placeholder. + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, end); + node = nextSibling; + } + }; + + // remove the nodes in the DOM representing this metamorph. + // + // this includes the starting and ending placeholders. + removeFunc = function() { + var start = realNode(document.getElementById(this.start)); + var end = document.getElementById(this.end); + + this.html(''); + start.parentNode.removeChild(start); + end.parentNode.removeChild(end); + }; + + appendToFunc = function(parentNode) { + var node = firstNodeFor(parentNode, this.outerHTML()); + var nextSibling; + + while (node) { + nextSibling = node.nextSibling; + parentNode.appendChild(node); + node = nextSibling; + } + }; + + afterFunc = function(html) { + // get the real starting node. see realNode for details. + var end = document.getElementById(this.end); + var insertBefore = end.nextSibling; + var parentNode = end.parentNode; + var nextSibling; + var node; + + // get the first node for the HTML string, even in cases like + // tables and lists where a simple innerHTML on a div would + // swallow some of the content. + node = firstNodeFor(parentNode, html); + + // copy the nodes for the HTML between the starting and ending + // placeholder. + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, insertBefore); + node = nextSibling; + } + }; + + prependFunc = function(html) { + var start = document.getElementById(this.start); + var parentNode = start.parentNode; + var nextSibling; + var node; + + node = firstNodeFor(parentNode, html); + var insertBefore = start.nextSibling; + + while (node) { + nextSibling = node.nextSibling; + parentNode.insertBefore(node, insertBefore); + node = nextSibling; + } + }; + } + + Metamorph.prototype.html = function(html) { + this.checkRemoved(); + if (html === undefined) { return this.innerHTML; } + + htmlFunc.call(this, html); + + this.innerHTML = html; + }; + + Metamorph.prototype.replaceWith = function(html) { + this.checkRemoved(); + htmlFunc.call(this, html, true); + }; + + Metamorph.prototype.remove = removeFunc; + Metamorph.prototype.outerHTML = outerHTMLFunc; + Metamorph.prototype.appendTo = appendToFunc; + Metamorph.prototype.after = afterFunc; + Metamorph.prototype.prepend = prependFunc; + Metamorph.prototype.startTag = startTagFunc; + Metamorph.prototype.endTag = endTagFunc; + + Metamorph.prototype.isRemoved = function() { + var before = document.getElementById(this.start); + var after = document.getElementById(this.end); + + return !before || !after; + }; + + Metamorph.prototype.checkRemoved = function() { + if (this.isRemoved()) { + throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); + } + }; + + return Metamorph; + }); + +})(); + +(function() { +/** +@module ember +@submodule ember-handlebars-compiler +*/ + +// Eliminate dependency on any Ember to simplify precompilation workflow +var objectCreate = Object.create || function(parent) { + function F() {} + F.prototype = parent; + return new F(); +}; + +var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars); +if(!Handlebars && typeof require === 'function') { + Handlebars = require('handlebars'); +} + +Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars) +Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4, COMPILER_REVISION expected: 3, got: " + Handlebars.COMPILER_REVISION + " – Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 3); + +/** + Prepares the Handlebars templating library for use inside Ember's view + system. + + The `Ember.Handlebars` object is the standard Handlebars library, extended to + use Ember's `get()` method instead of direct property access, which allows + computed properties to be used inside templates. + + To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. + This will return a function that can be used by `Ember.View` for rendering. + + @class Handlebars + @namespace Ember +*/ +Ember.Handlebars = objectCreate(Handlebars); + +function makeBindings(options) { + var hash = options.hash, + hashType = options.hashTypes; + + for (var prop in hash) { + if (hashType[prop] === 'ID') { + hash[prop + 'Binding'] = hash[prop]; + hashType[prop + 'Binding'] = 'STRING'; + delete hash[prop]; + delete hashType[prop]; + } + } +} + +/** + Register a bound helper or custom view helper. + + ## Simple bound helper example + + ```javascript + Ember.Handlebars.helper('capitalize', function(value) { + return value.toUpperCase(); + }); + ``` + + The above bound helper can be used inside of templates as follows: + + ```handlebars + {{capitalize name}} + ``` + + In this case, when the `name` property of the template's context changes, + the rendered value of the helper will update to reflect this change. + + For more examples of bound helpers, see documentation for + `Ember.Handlebars.registerBoundHelper`. + + ## Custom view helper example + + Assuming a view subclass named `App.CalenderView` were defined, a helper + for rendering instances of this view could be registered as follows: + + ```javascript + Ember.Handlebars.helper('calendar', App.CalendarView): + ``` + + The above bound helper can be used inside of templates as follows: + + ```handlebars + {{calendar}} + ``` + + Which is functionally equivalent to: + + ```handlebars + {{view App.CalendarView}} + ``` + + Options in the helper will be passed to the view in exactly the same + manner as with the `view` helper. + + @method helper + @for Ember.Handlebars + @param {String} name + @param {Function|Ember.View} function or view class constructor + @param {String} dependentKeys* +*/ +Ember.Handlebars.helper = function(name, value) { + if (Ember.Component.detect(value)) { + Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", name.match(/-/)); + + var proto = value.proto(); + if (!proto.layoutName && !proto.templateName) { + value.reopen({ + layoutName: 'components/' + name + }); + } + } + + if (Ember.View.detect(value)) { + Ember.Handlebars.registerHelper(name, function(options) { + Ember.assert("You can only pass attributes as parameters (not values) to a application-defined helper", arguments.length < 2); + makeBindings(options); + return Ember.Handlebars.helpers.view.call(this, value, options); + }); + } else { + Ember.Handlebars.registerBoundHelper.apply(null, arguments); + } +} + +/** +@class helpers +@namespace Ember.Handlebars +*/ +Ember.Handlebars.helpers = objectCreate(Handlebars.helpers); + +/** + Override the the opcode compiler and JavaScript compiler for Handlebars. + + @class Compiler + @namespace Ember.Handlebars + @private + @constructor +*/ +Ember.Handlebars.Compiler = function() {}; + +// Handlebars.Compiler doesn't exist in runtime-only +if (Handlebars.Compiler) { + Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); +} + +Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler; + +/** + @class JavaScriptCompiler + @namespace Ember.Handlebars + @private + @constructor +*/ +Ember.Handlebars.JavaScriptCompiler = function() {}; + +// Handlebars.JavaScriptCompiler doesn't exist in runtime-only +if (Handlebars.JavaScriptCompiler) { + Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); + Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler; +} + + +Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; + + +Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { + return "''"; +}; + +/** + @private + + Override the default buffer for Ember Handlebars. By default, Handlebars + creates an empty String at the beginning of each invocation and appends to + it. Ember's Handlebars overrides this to append to a single shared buffer. + + @method appendToBuffer + @param string {String} +*/ +Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { + return "data.buffer.push("+string+");"; +}; + +var prefix = "ember" + (+new Date()), incr = 1; + +/** + @private + + Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that + all simple mustaches in Ember's Handlebars will also set up an observer to + keep the DOM up to date when the underlying property changes. + + @method mustache + @for Ember.Handlebars.Compiler + @param mustache +*/ +Ember.Handlebars.Compiler.prototype.mustache = function(mustache) { + if (mustache.isHelper && mustache.id.string === 'control') { + mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); + mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]); + } else if (mustache.params.length || mustache.hash) { + // no changes required + } else { + var id = new Handlebars.AST.IdNode(['_triageMustache']); + + // Update the mustache node to include a hash value indicating whether the original node + // was escaped. This will allow us to properly escape values when the underlying value + // changes and we need to re-render the value. + if(!mustache.escaped) { + mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); + mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); + } + mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); + } + + return Handlebars.Compiler.prototype.mustache.call(this, mustache); +}; + +/** + Used for precompilation of Ember Handlebars templates. This will not be used + during normal app execution. + + @method precompile + @for Ember.Handlebars + @static + @param {String} string The template to precompile +*/ +Ember.Handlebars.precompile = function(string) { + var ast = Handlebars.parse(string); + + var options = { + knownHelpers: { + action: true, + unbound: true, + bindAttr: true, + template: true, + view: true, + _triageMustache: true + }, + data: true, + stringParams: true + }; + + var environment = new Ember.Handlebars.Compiler().compile(ast, options); + return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); +}; + +// We don't support this for Handlebars runtime-only +if (Handlebars.compile) { + /** + The entry point for Ember Handlebars. This replaces the default + `Handlebars.compile` and turns on template-local data and String + parameters. + + @method compile + @for Ember.Handlebars + @static + @param {String} string The template to compile + @return {Function} + */ + Ember.Handlebars.compile = function(string) { + var ast = Handlebars.parse(string); + var options = { data: true, stringParams: true }; + var environment = new Ember.Handlebars.Compiler().compile(ast, options); + var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); + + return Ember.Handlebars.template(templateSpec); + }; +} + + +})(); + +(function() { +var slice = Array.prototype.slice; + +/** + @private + + If a path starts with a reserved keyword, returns the root + that should be used. + + @method normalizePath + @for Ember + @param root {Object} + @param path {String} + @param data {Hash} +*/ +var normalizePath = Ember.Handlebars.normalizePath = function(root, path, data) { + var keywords = (data && data.keywords) || {}, + keyword, isKeyword; + + // Get the first segment of the path. For example, if the + // path is "foo.bar.baz", returns "foo". + keyword = path.split('.', 1)[0]; + + // Test to see if the first path is a keyword that has been + // passed along in the view's data hash. If so, we will treat + // that object as the new root. + if (keywords.hasOwnProperty(keyword)) { + // Look up the value in the template's data hash. + root = keywords[keyword]; + isKeyword = true; + + // Handle cases where the entire path is the reserved + // word. In that case, return the object itself. + if (path === keyword) { + path = ''; + } else { + // Strip the keyword from the path and look up + // the remainder from the newly found root. + path = path.substr(keyword.length+1); + } + } + + return { root: root, path: path, isKeyword: isKeyword }; +}; + + +/** + Lookup both on root and on window. If the path starts with + a keyword, the corresponding object will be looked up in the + template's data hash and used to resolve the path. + + @method get + @for Ember.Handlebars + @param {Object} root The object to look up the property on + @param {String} path The path to be lookedup + @param {Object} options The template's option hash +*/ +var handlebarsGet = Ember.Handlebars.get = function(root, path, options) { + var data = options && options.data, + normalizedPath = normalizePath(root, path, data), + value; + + // In cases where the path begins with a keyword, change the + // root to the value represented by that keyword, and ensure + // the path is relative to it. + root = normalizedPath.root; + path = normalizedPath.path; + + value = Ember.get(root, path); + + // If the path starts with a capital letter, look it up on Ember.lookup, + // which defaults to the `window` object in browsers. + if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) { + value = Ember.get(Ember.lookup, path); + } + return value; +}; +Ember.Handlebars.getPath = Ember.deprecateFunc('`Ember.Handlebars.getPath` has been changed to `Ember.Handlebars.get` for consistency.', Ember.Handlebars.get); + +Ember.Handlebars.resolveParams = function(context, params, options) { + var resolvedParams = [], types = options.types, param, type; + + for (var i=0, l=params.length; i<l; i++) { + param = params[i]; + type = types[i]; + + if (type === 'ID') { + resolvedParams.push(handlebarsGet(context, param, options)); + } else { + resolvedParams.push(param); + } + } + + return resolvedParams; +}; + +Ember.Handlebars.resolveHash = function(context, hash, options) { + var resolvedHash = {}, types = options.hashTypes, type; + + for (var key in hash) { + if (!hash.hasOwnProperty(key)) { continue; } + + type = types[key]; + + if (type === 'ID') { + resolvedHash[key] = handlebarsGet(context, hash[key], options); + } else { + resolvedHash[key] = hash[key]; + } + } + + return resolvedHash; +}; + +/** + @private + + Registers a helper in Handlebars that will be called if no property with the + given name can be found on the current context object, and no helper with + that name is registered. + + This throws an exception with a more helpful error message so the user can + track down where the problem is happening. + + @method helperMissing + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options +*/ +Ember.Handlebars.registerHelper('helperMissing', function(path, options) { + var error, view = ""; + + error = "%@ Handlebars error: Could not find property '%@' on object %@."; + if (options.data){ + view = options.data.view; + } + throw new Ember.Error(Ember.String.fmt(error, [view, path, this])); +}); + +/** + Register a bound handlebars helper. Bound helpers behave similarly to regular + handlebars helpers, with the added ability to re-render when the underlying data + changes. + + ## Simple example + + ```javascript + Ember.Handlebars.registerBoundHelper('capitalize', function(value) { + return value.toUpperCase(); + }); + ``` + + The above bound helper can be used inside of templates as follows: + + ```handlebars + {{capitalize name}} + ``` + + In this case, when the `name` property of the template's context changes, + the rendered value of the helper will update to reflect this change. + + ## Example with options + + Like normal handlebars helpers, bound helpers have access to the options + passed into the helper call. + + ```javascript + Ember.Handlebars.registerBoundHelper('repeat', function(value, options) { + var count = options.hash.count; + var a = []; + while(a.length < count){ + a.push(value); + } + return a.join(''); + }); + ``` + + This helper could be used in a template as follows: + + ```handlebars + {{repeat text count=3}} + ``` + + ## Example with bound options + + Bound hash options are also supported. Example: + + ```handlebars + {{repeat text countBinding="numRepeats"}} + ``` + + In this example, count will be bound to the value of + the `numRepeats` property on the context. If that property + changes, the helper will be re-rendered. + + ## Example with extra dependencies + + The `Ember.Handlebars.registerBoundHelper` method takes a variable length + third parameter which indicates extra dependencies on the passed in value. + This allows the handlebars helper to update when these dependencies change. + + ```javascript + Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { + return value.get('name').toUpperCase(); + }, 'name'); + ``` + + ## Example with multiple bound properties + + `Ember.Handlebars.registerBoundHelper` supports binding to + multiple properties, e.g.: + + ```javascript + Ember.Handlebars.registerBoundHelper('concatenate', function() { + var values = arguments[arguments.length - 1]; + return values.join('||'); + }); + ``` + + Which allows for template syntax such as `{{concatenate prop1 prop2}}` or + `{{concatenate prop1 prop2 prop3}}`. If any of the properties change, + the helpr will re-render. Note that dependency keys cannot be + using in conjunction with multi-property helpers, since it is ambiguous + which property the dependent keys would belong to. + + ## Use with unbound helper + + The `{{unbound}}` helper can be used with bound helper invocations + to render them in their unbound form, e.g. + + ```handlebars + {{unbound capitalize name}} + ``` + + In this example, if the name property changes, the helper + will not re-render. + + ## Use with blocks not supported + + Bound helpers do not support use with Handlebars blocks or + the addition of child views of any kind. + + @method registerBoundHelper + @for Ember.Handlebars + @param {String} name + @param {Function} function + @param {String} dependentKeys* +*/ +Ember.Handlebars.registerBoundHelper = function(name, fn) { + var dependentKeys = slice.call(arguments, 2); + + function helper() { + var properties = slice.call(arguments, 0, -1), + numProperties = properties.length, + options = arguments[arguments.length - 1], + normalizedProperties = [], + data = options.data, + hash = options.hash, + view = data.view, + currentContext = (options.contexts && options.contexts[0]) || this, + normalized, + pathRoot, path, + loc, hashOption; + + Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); + + // Detect bound options (e.g. countBinding="otherCount") + hash.boundOptions = {}; + for (hashOption in hash) { + if (!hash.hasOwnProperty(hashOption)) { continue; } + + if (Ember.IS_BINDING.test(hashOption) && typeof hash[hashOption] === 'string') { + // Lop off 'Binding' suffix. + hash.boundOptions[hashOption.slice(0, -7)] = hash[hashOption]; + } + } + + // Expose property names on data.properties object. + data.properties = []; + for (loc = 0; loc < numProperties; ++loc) { + data.properties.push(properties[loc]); + normalizedProperties.push(normalizePath(currentContext, properties[loc], data)); + } + + if (data.isUnbound) { + return evaluateUnboundHelper(this, fn, normalizedProperties, options); + } + + if (dependentKeys.length === 0) { + return evaluateMultiPropertyBoundHelper(currentContext, fn, normalizedProperties, options); + } + + Ember.assert("Dependent keys can only be used with single-property helpers.", properties.length === 1); + + normalized = normalizedProperties[0]; + + pathRoot = normalized.root; + path = normalized.path; + + var bindView = new Ember._SimpleHandlebarsView( + path, pathRoot, !options.hash.unescaped, options.data + ); + + bindView.normalizedValue = function() { + var value = Ember._SimpleHandlebarsView.prototype.normalizedValue.call(bindView); + return fn.call(view, value, options); + }; + + view.appendChild(bindView); + + view.registerObserver(pathRoot, path, bindView, bindView.rerender); + + for (var i=0, l=dependentKeys.length; i<l; i++) { + view.registerObserver(pathRoot, path + '.' + dependentKeys[i], bindView, bindView.rerender); + } + } + + helper._rawFunction = fn; + Ember.Handlebars.registerHelper(name, helper); +}; + +/** + @private + + Renders the unbound form of an otherwise bound helper function. + + @method evaluateMultiPropertyBoundHelper + @param {Function} fn + @param {Object} context + @param {Array} normalizedProperties + @param {String} options +*/ +function evaluateMultiPropertyBoundHelper(context, fn, normalizedProperties, options) { + var numProperties = normalizedProperties.length, + data = options.data, + view = data.view, + hash = options.hash, + boundOptions = hash.boundOptions, + watchedProperties, + boundOption, bindView, loc, property, len; + + bindView = new Ember._SimpleHandlebarsView(null, null, !hash.unescaped, data); + bindView.normalizedValue = function() { + var args = [], boundOption; + + // Copy over bound options. + for (boundOption in boundOptions) { + if (!boundOptions.hasOwnProperty(boundOption)) { continue; } + property = normalizePath(context, boundOptions[boundOption], data); + bindView.path = property.path; + bindView.pathRoot = property.root; + hash[boundOption] = Ember._SimpleHandlebarsView.prototype.normalizedValue.call(bindView); + } + + for (loc = 0; loc < numProperties; ++loc) { + property = normalizedProperties[loc]; + bindView.path = property.path; + bindView.pathRoot = property.root; + args.push(Ember._SimpleHandlebarsView.prototype.normalizedValue.call(bindView)); + } + args.push(options); + return fn.apply(context, args); + }; + + view.appendChild(bindView); + + // Assemble liast of watched properties that'll re-render this helper. + watchedProperties = []; + for (boundOption in boundOptions) { + if (boundOptions.hasOwnProperty(boundOption)) { + watchedProperties.push(normalizePath(context, boundOptions[boundOption], data)); + } + } + watchedProperties = watchedProperties.concat(normalizedProperties); + + // Observe each property. + for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { + property = watchedProperties[loc]; + view.registerObserver(property.root, property.path, bindView, bindView.rerender); + } + +} + +/** + @private + + Renders the unbound form of an otherwise bound helper function. + + @method evaluateUnboundHelper + @param {Function} fn + @param {Object} context + @param {Array} normalizedProperties + @param {String} options +*/ +function evaluateUnboundHelper(context, fn, normalizedProperties, options) { + var args = [], hash = options.hash, boundOptions = hash.boundOptions, loc, len, property, boundOption; + + for (boundOption in boundOptions) { + if (!boundOptions.hasOwnProperty(boundOption)) { continue; } + hash[boundOption] = Ember.Handlebars.get(context, boundOptions[boundOption], options); + } + + for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { + property = normalizedProperties[loc]; + args.push(Ember.Handlebars.get(context, property.path, options)); + } + args.push(options); + return fn.apply(context, args); +} + +/** + @private + + Overrides Handlebars.template so that we can distinguish + user-created, top-level templates from inner contexts. + + @method template + @for Ember.Handlebars + @param {String} template spec +*/ +Ember.Handlebars.template = function(spec){ + var t = Handlebars.template(spec); + t.isTop = true; + return t; +}; + +})(); + + + +(function() { +/** + @method htmlSafe + @for Ember.String + @static +*/ +Ember.String.htmlSafe = function(str) { + return new Handlebars.SafeString(str); +}; + +var htmlSafe = Ember.String.htmlSafe; + +if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + + /** + See `Ember.String.htmlSafe`. + + @method htmlSafe + @for String + */ + String.prototype.htmlSafe = function() { + return htmlSafe(this); + }; +} + +})(); + + + +(function() { +Ember.Handlebars.resolvePaths = function(options) { + var ret = [], + contexts = options.contexts, + roots = options.roots, + data = options.data; + + for (var i=0, l=contexts.length; i<l; i++) { + ret.push( Ember.Handlebars.get(roots[i], contexts[i], { data: data }) ); + } + + return ret; +}; + +})(); + + + +(function() { +/*jshint newcap:false*/ +/** +@module ember +@submodule ember-handlebars +*/ + +var set = Ember.set, get = Ember.get; +var Metamorph = requireModule('metamorph'); + +function notifyMutationListeners() { + Ember.run.once(Ember.View, 'notifyMutationListeners'); +} + +// DOMManager should just abstract dom manipulation between jquery and metamorph +var DOMManager = { + remove: function(view) { + view.morph.remove(); + notifyMutationListeners(); + }, + + prepend: function(view, html) { + view.morph.prepend(html); + notifyMutationListeners(); + }, + + after: function(view, html) { + view.morph.after(html); + notifyMutationListeners(); + }, + + html: function(view, html) { + view.morph.html(html); + notifyMutationListeners(); + }, + + // This is messed up. + replace: function(view) { + var morph = view.morph; + + view.transitionTo('preRender'); + + Ember.run.schedule('render', this, function() { + if (view.isDestroying) { return; } + + view.clearRenderedChildren(); + var buffer = view.renderToBuffer(); + + view.invokeRecursively(function(view) { + view.propertyWillChange('element'); + }); + view.triggerRecursively('willInsertElement'); + + morph.replaceWith(buffer.string()); + view.transitionTo('inDOM'); + + view.invokeRecursively(function(view) { + view.propertyDidChange('element'); + }); + view.triggerRecursively('didInsertElement'); + + notifyMutationListeners(); + }); + }, + + empty: function(view) { + view.morph.html(""); + notifyMutationListeners(); + } +}; + +// The `morph` and `outerHTML` properties are internal only +// and not observable. + +/** + @class _Metamorph + @namespace Ember + @extends Ember.Mixin + @private +*/ +Ember._Metamorph = Ember.Mixin.create({ + isVirtual: true, + tagName: '', + + instrumentName: 'metamorph', + + init: function() { + this._super(); + this.morph = Metamorph(); + Ember.deprecate('Supplying a tagName to Metamorph views is unreliable and is deprecated. You may be setting the tagName on a Handlebars helper that creates a Metamorph.', !this.tagName); + }, + + beforeRender: function(buffer) { + buffer.push(this.morph.startTag()); + buffer.pushOpeningTag(); + }, + + afterRender: function(buffer) { + buffer.pushClosingTag(); + buffer.push(this.morph.endTag()); + }, + + createElement: function() { + var buffer = this.renderToBuffer(); + this.outerHTML = buffer.string(); + this.clearBuffer(); + }, + + domManager: DOMManager +}); + +/** + @class _MetamorphView + @namespace Ember + @extends Ember.View + @uses Ember._Metamorph + @private +*/ +Ember._MetamorphView = Ember.View.extend(Ember._Metamorph); + +/** + @class _SimpleMetamorphView + @namespace Ember + @extends Ember.CoreView + @uses Ember._Metamorph + @private +*/ +Ember._SimpleMetamorphView = Ember.CoreView.extend(Ember._Metamorph); + + +})(); + + + +(function() { +/*globals Handlebars */ +/*jshint newcap:false*/ +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set, handlebarsGet = Ember.Handlebars.get; +var Metamorph = requireModule('metamorph'); +function SimpleHandlebarsView(path, pathRoot, isEscaped, templateData) { + this.path = path; + this.pathRoot = pathRoot; + this.isEscaped = isEscaped; + this.templateData = templateData; + + this.morph = Metamorph(); + this.state = 'preRender'; + this.updateId = null; +} + +Ember._SimpleHandlebarsView = SimpleHandlebarsView; + +SimpleHandlebarsView.prototype = { + isVirtual: true, + isView: true, + + destroy: function () { + if (this.updateId) { + Ember.run.cancel(this.updateId); + this.updateId = null; + } + this.morph = null; + }, + + propertyWillChange: Ember.K, + + propertyDidChange: Ember.K, + + normalizedValue: function() { + var path = this.path, + pathRoot = this.pathRoot, + result, templateData; + + // Use the pathRoot as the result if no path is provided. This + // happens if the path is `this`, which gets normalized into + // a `pathRoot` of the current Handlebars context and a path + // of `''`. + if (path === '') { + result = pathRoot; + } else { + templateData = this.templateData; + result = handlebarsGet(pathRoot, path, { data: templateData }); + } + + return result; + }, + + renderToBuffer: function(buffer) { + var string = ''; + + string += this.morph.startTag(); + string += this.render(); + string += this.morph.endTag(); + + buffer.push(string); + }, + + render: function() { + // If not invoked via a triple-mustache ({{{foo}}}), escape + // the content of the template. + var escape = this.isEscaped; + var result = this.normalizedValue(); + + if (result === null || result === undefined) { + result = ""; + } else if (!(result instanceof Handlebars.SafeString)) { + result = String(result); + } + + if (escape) { result = Handlebars.Utils.escapeExpression(result); } + return result; + }, + + rerender: function() { + switch(this.state) { + case 'preRender': + case 'destroying': + break; + case 'inBuffer': + throw new Ember.Error("Something you did tried to replace an {{expression}} before it was inserted into the DOM."); + case 'hasElement': + case 'inDOM': + this.updateId = Ember.run.scheduleOnce('render', this, 'update'); + break; + } + + return this; + }, + + update: function () { + this.updateId = null; + this.morph.html(this.render()); + }, + + transitionTo: function(state) { + this.state = state; + } +}; + +var states = Ember.View.cloneStates(Ember.View.states), merge = Ember.merge; + +merge(states._default, { + rerenderIfNeeded: Ember.K +}); + +merge(states.inDOM, { + rerenderIfNeeded: function(view) { + if (view.normalizedValue() !== view._lastNormalizedValue) { + view.rerender(); + } + } +}); + +/** + `Ember._HandlebarsBoundView` is a private view created by the Handlebars + `{{bind}}` helpers that is used to keep track of bound properties. + + Every time a property is bound using a `{{mustache}}`, an anonymous subclass + of `Ember._HandlebarsBoundView` is created with the appropriate sub-template + and context set up. When the associated property changes, just the template + for this view will re-render. + + @class _HandlebarsBoundView + @namespace Ember + @extends Ember._MetamorphView + @private +*/ +Ember._HandlebarsBoundView = Ember._MetamorphView.extend({ + instrumentName: 'boundHandlebars', + states: states, + + /** + The function used to determine if the `displayTemplate` or + `inverseTemplate` should be rendered. This should be a function that takes + a value and returns a Boolean. + + @property shouldDisplayFunc + @type Function + @default null + */ + shouldDisplayFunc: null, + + /** + Whether the template rendered by this view gets passed the context object + of its parent template, or gets passed the value of retrieving `path` + from the `pathRoot`. + + For example, this is true when using the `{{#if}}` helper, because the + template inside the helper should look up properties relative to the same + object as outside the block. This would be `false` when used with `{{#with + foo}}` because the template should receive the object found by evaluating + `foo`. + + @property preserveContext + @type Boolean + @default false + */ + preserveContext: false, + + /** + If `preserveContext` is true, this is the object that will be used + to render the template. + + @property previousContext + @type Object + */ + previousContext: null, + + /** + The template to render when `shouldDisplayFunc` evaluates to `true`. + + @property displayTemplate + @type Function + @default null + */ + displayTemplate: null, + + /** + The template to render when `shouldDisplayFunc` evaluates to `false`. + + @property inverseTemplate + @type Function + @default null + */ + inverseTemplate: null, + + + /** + The path to look up on `pathRoot` that is passed to + `shouldDisplayFunc` to determine which template to render. + + In addition, if `preserveContext` is `false,` the object at this path will + be passed to the template when rendering. + + @property path + @type String + @default null + */ + path: null, + + /** + The object from which the `path` will be looked up. Sometimes this is the + same as the `previousContext`, but in cases where this view has been + generated for paths that start with a keyword such as `view` or + `controller`, the path root will be that resolved object. + + @property pathRoot + @type Object + */ + pathRoot: null, + + normalizedValue: function() { + var path = get(this, 'path'), + pathRoot = get(this, 'pathRoot'), + valueNormalizer = get(this, 'valueNormalizerFunc'), + result, templateData; + + // Use the pathRoot as the result if no path is provided. This + // happens if the path is `this`, which gets normalized into + // a `pathRoot` of the current Handlebars context and a path + // of `''`. + if (path === '') { + result = pathRoot; + } else { + templateData = get(this, 'templateData'); + result = handlebarsGet(pathRoot, path, { data: templateData }); + } + + return valueNormalizer ? valueNormalizer(result) : result; + }, + + rerenderIfNeeded: function() { + this.currentState.rerenderIfNeeded(this); + }, + + /** + Determines which template to invoke, sets up the correct state based on + that logic, then invokes the default `Ember.View` `render` implementation. + + This method will first look up the `path` key on `pathRoot`, + then pass that value to the `shouldDisplayFunc` function. If that returns + `true,` the `displayTemplate` function will be rendered to DOM. Otherwise, + `inverseTemplate`, if specified, will be rendered. + + For example, if this `Ember._HandlebarsBoundView` represented the `{{#with + foo}}` helper, it would look up the `foo` property of its context, and + `shouldDisplayFunc` would always return true. The object found by looking + up `foo` would be passed to `displayTemplate`. + + @method render + @param {Ember.RenderBuffer} buffer + */ + render: function(buffer) { + // If not invoked via a triple-mustache ({{{foo}}}), escape + // the content of the template. + var escape = get(this, 'isEscaped'); + + var shouldDisplay = get(this, 'shouldDisplayFunc'), + preserveContext = get(this, 'preserveContext'), + context = get(this, 'previousContext'); + + var inverseTemplate = get(this, 'inverseTemplate'), + displayTemplate = get(this, 'displayTemplate'); + + var result = this.normalizedValue(); + this._lastNormalizedValue = result; + + // First, test the conditional to see if we should + // render the template or not. + if (shouldDisplay(result)) { + set(this, 'template', displayTemplate); + + // If we are preserving the context (for example, if this + // is an #if block, call the template with the same object. + if (preserveContext) { + set(this, '_context', context); + } else { + // Otherwise, determine if this is a block bind or not. + // If so, pass the specified object to the template + if (displayTemplate) { + set(this, '_context', result); + } else { + // This is not a bind block, just push the result of the + // expression to the render context and return. + if (result === null || result === undefined) { + result = ""; + } else if (!(result instanceof Handlebars.SafeString)) { + result = String(result); + } + + if (escape) { result = Handlebars.Utils.escapeExpression(result); } + buffer.push(result); + return; + } + } + } else if (inverseTemplate) { + set(this, 'template', inverseTemplate); + + if (preserveContext) { + set(this, '_context', context); + } else { + set(this, '_context', result); + } + } else { + set(this, 'template', function() { return ''; }); + } + + return this._super(buffer); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; +var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; +var forEach = Ember.ArrayPolyfills.forEach; + +var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers; + +function exists(value){ + return !Ember.isNone(value); +} + +// Binds a property into the DOM. This will create a hook in DOM that the +// KVO system will look for and update if the property changes. +function bind(property, options, preserveContext, shouldDisplay, valueNormalizer, childProperties) { + var data = options.data, + fn = options.fn, + inverse = options.inverse, + view = data.view, + currentContext = this, + normalized, observer, i; + + normalized = normalizePath(currentContext, property, data); + + // Set up observers for observable objects + if ('object' === typeof this) { + if (data.insideGroup) { + observer = function() { + Ember.run.once(view, 'rerender'); + }; + + var template, context, result = handlebarsGet(currentContext, property, options); + + result = valueNormalizer(result); + + context = preserveContext ? currentContext : result; + if (shouldDisplay(result)) { + template = fn; + } else if (inverse) { + template = inverse; + } + + template(context, { data: options.data }); + } else { + // Create the view that will wrap the output of this template/property + // and add it to the nearest view's childViews array. + // See the documentation of Ember._HandlebarsBoundView for more. + var bindView = view.createChildView(Ember._HandlebarsBoundView, { + preserveContext: preserveContext, + shouldDisplayFunc: shouldDisplay, + valueNormalizerFunc: valueNormalizer, + displayTemplate: fn, + inverseTemplate: inverse, + path: property, + pathRoot: currentContext, + previousContext: currentContext, + isEscaped: !options.hash.unescaped, + templateData: options.data + }); + + view.appendChild(bindView); + + observer = function() { + Ember.run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + }; + } + + // Observes the given property on the context and + // tells the Ember._HandlebarsBoundView to re-render. If property + // is an empty string, we are printing the current context + // object ({{this}}) so updating it is not our responsibility. + if (normalized.path !== '') { + view.registerObserver(normalized.root, normalized.path, observer); + if (childProperties) { + for (i=0; i<childProperties.length; i++) { + view.registerObserver(normalized.root, normalized.path+'.'+childProperties[i], observer); + } + } + } + } else { + // The object is not observable, so just render it out and + // be done with it. + data.buffer.push(handlebarsGet(currentContext, property, options)); + } +} + +function simpleBind(property, options) { + var data = options.data, + view = data.view, + currentContext = this, + normalized, observer; + + normalized = normalizePath(currentContext, property, data); + + // Set up observers for observable objects + if ('object' === typeof this) { + if (data.insideGroup) { + observer = function() { + Ember.run.once(view, 'rerender'); + }; + + var result = handlebarsGet(currentContext, property, options); + if (result === null || result === undefined) { result = ""; } + data.buffer.push(result); + } else { + var bindView = new Ember._SimpleHandlebarsView( + property, currentContext, !options.hash.unescaped, options.data + ); + + bindView._parentView = view; + view.appendChild(bindView); + + observer = function() { + Ember.run.scheduleOnce('render', bindView, 'rerender'); + }; + } + + // Observes the given property on the context and + // tells the Ember._HandlebarsBoundView to re-render. If property + // is an empty string, we are printing the current context + // object ({{this}}) so updating it is not our responsibility. + if (normalized.path !== '') { + view.registerObserver(normalized.root, normalized.path, observer); + } + } else { + // The object is not observable, so just render it out and + // be done with it. + data.buffer.push(handlebarsGet(currentContext, property, options)); + } +} + +/** + @private + + '_triageMustache' is used internally select between a binding and helper for + the given context. Until this point, it would be hard to determine if the + mustache is a property reference or a regular helper reference. This triage + helper resolves that. + + This would not be typically invoked by directly. + + @method _triageMustache + @for Ember.Handlebars.helpers + @param {String} property Property/helperID to triage + @param {Function} fn Context to provide for rendering + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('_triageMustache', function(property, fn) { + Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2); + if (helpers[property]) { + return helpers[property].call(this, fn); + } + else { + return helpers.bind.apply(this, arguments); + } +}); + +/** + @private + + `bind` can be used to display a value, then update that value if it + changes. For example, if you wanted to print the `title` property of + `content`: + + ```handlebars + {{bind "content.title"}} + ``` + + This will return the `title` property as a string, then create a new observer + at the specified path. If it changes, it will update the value in DOM. Note + that if you need to support IE7 and IE8 you must modify the model objects + properties using `Ember.get()` and `Ember.set()` for this to work as it + relies on Ember's KVO system. For all other browsers this will be handled for + you automatically. + + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('bind', function(property, options) { + Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); + + var context = (options.contexts && options.contexts[0]) || this; + + if (!options.fn) { + return simpleBind.call(context, property, options); + } + + return bind.call(context, property, options, false, exists); +}); + +/** + @private + + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. + + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` + + @method boundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('boundIf', function(property, fn) { + var context = (fn.contexts && fn.contexts[0]) || this; + var func = function(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } + + if (Ember.isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + }; + + return bind.call(context, property, fn, true, func, func, ['isTruthy', 'length']); +}); + +/** + @method with + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('with', function(context, options) { + if (arguments.length === 4) { + var keywordName, path, rootPath, normalized; + + Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); + options = arguments[3]; + keywordName = arguments[2]; + path = arguments[0]; + + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + + if (Ember.isGlobalPath(path)) { + Ember.bind(options.data.keywords, keywordName, path); + } else { + normalized = normalizePath(this, path, options.data); + path = normalized.path; + rootPath = normalized.root; + + // This is a workaround for the fact that you cannot bind separate objects + // together. When we implement that functionality, we should use it here. + var contextKey = Ember.$.expando + Ember.guidFor(rootPath); + options.data.keywords[contextKey] = rootPath; + + // if the path is '' ("this"), just bind directly to the current context + var contextPath = path ? contextKey + '.' + path : contextKey; + Ember.bind(options.data.keywords, keywordName, contextPath); + } + + return bind.call(this, path, options, true, exists); + } else { + Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + return helpers.bind.call(options.contexts[0], context, options); + } +}); + + +/** + See `boundIf` + + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('if', function(context, options) { + Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); + Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); + + return helpers.boundIf.call(options.contexts[0], context, options); +}); + +/** + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('unless', function(context, options) { + Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); + Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); + + var fn = options.fn, inverse = options.inverse; + + options.fn = inverse; + options.inverse = fn; + + return helpers.boundIf.call(options.contexts[0], context, options); +}); + +/** + `bindAttr` allows you to create a binding between DOM element attributes and + Ember objects. For example: + + ```handlebars + <img {{bindAttr src="imageUrl" alt="imageTitle"}}> + ``` + + The above handlebars template will fill the `<img>`'s `src` attribute will + the value of the property referenced with `"imageUrl"` and its `alt` + attribute with the value of the property referenced with `"imageTitle"`. + + If the rendering context of this template is the following object: + + ```javascript + { + imageUrl: 'http://lolcats.info/haz-a-funny', + imageTitle: 'A humorous image of a cat' + } + ``` + + The resulting HTML output will be: + + ```html + <img src="http://lolcats.info/haz-a-funny" alt="A humorous image of a cat"> + ``` + + `bindAttr` cannot redeclare existing DOM element attributes. The use of `src` + in the following `bindAttr` example will be ignored and the hard coded value + of `src="/failwhale.gif"` will take precedence: + + ```handlebars + <img src="/failwhale.gif" {{bindAttr src="imageUrl" alt="imageTitle"}}> + ``` + + ### `bindAttr` and the `class` attribute + + `bindAttr` supports a special syntax for handling a number of cases unique + to the `class` DOM element attribute. The `class` attribute combines + multiple discreet values into a single attribute as a space-delimited + list of strings. Each string can be: + + * a string return value of an object's property. + * a boolean return value of an object's property + * a hard-coded value + + A string return value works identically to other uses of `bindAttr`. The + return value of the property will become the value of the attribute. For + example, the following view and template: + + ```javascript + AView = Ember.View.extend({ + someProperty: function(){ + return "aValue"; + }.property() + }) + ``` + + ```handlebars + <img {{bindAttr class="view.someProperty}}> + ``` + + Result in the following rendered output: + + ```html + <img class="aValue"> + ``` + + A boolean return value will insert a specified class name if the property + returns `true` and remove the class name if the property returns `false`. + + A class name is provided via the syntax + `somePropertyName:class-name-if-true`. + + ```javascript + AView = Ember.View.extend({ + someBool: true + }) + ``` + + ```handlebars + <img {{bindAttr class="view.someBool:class-name-if-true"}}> + ``` + + Result in the following rendered output: + + ```html + <img class="class-name-if-true"> + ``` + + An additional section of the binding can be provided if you want to + replace the existing class instead of removing it when the boolean + value changes: + + ```handlebars + <img {{bindAttr class="view.someBool:class-name-if-true:class-name-if-false"}}> + ``` + + A hard-coded value can be used by prepending `:` to the desired + class name: `:class-name-to-always-apply`. + + ```handlebars + <img {{bindAttr class=":class-name-to-always-apply"}}> + ``` + + Results in the following rendered output: + + ```html + <img class="class-name-to-always-apply"> + ``` + + All three strategies - string return value, boolean return value, and + hard-coded value – can be combined in a single declaration: + + ```handlebars + <img {{bindAttr class=":class-name-to-always-apply view.someBool:class-name-if-true view.someProperty"}}> + ``` + + @method bindAttr + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('bindAttr', function(options) { + + var attrs = options.hash; + + Ember.assert("You must specify at least one hash argument to bindAttr", !!Ember.keys(attrs).length); + + var view = options.data.view; + var ret = []; + var ctx = this; + + // Generate a unique id for this element. This will be added as a + // data attribute to the element so it can be looked up when + // the bound property changes. + var dataId = ++Ember.uuid; + + // Handle classes differently, as we can bind multiple classes + var classBindings = attrs['class']; + if (classBindings != null) { + var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId, options); + + ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); + delete attrs['class']; + } + + var attrKeys = Ember.keys(attrs); + + // For each attribute passed, create an observer and emit the + // current value of the property as an attribute. + forEach.call(attrKeys, function(attr) { + var path = attrs[attr], + normalized; + + Ember.assert(fmt("You must provide a String for a bound attribute, not %@", [path]), typeof path === 'string'); + + normalized = normalizePath(ctx, path, options.data); + + var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), + type = Ember.typeOf(value); + + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); + + var observer, invoker; + + observer = function observer() { + var result = handlebarsGet(ctx, path, options); + + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), result === null || result === undefined || typeof result === 'number' || typeof result === 'string' || typeof result === 'boolean'); + + var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); + + // If we aren't able to find the element, it means the element + // to which we were bound has been removed from the view. + // In that case, we can assume the template has been re-rendered + // and we need to clean up the observer. + if (!elem || elem.length === 0) { + Ember.removeObserver(normalized.root, normalized.path, invoker); + return; + } + + Ember.View.applyAttributeBindings(elem, attr, result); + }; + + // Add an observer to the view for when the property changes. + // When the observer fires, find the element using the + // unique data id and update the attribute to the new value. + // Note: don't add observer when path is 'this' or path + // is whole keyword e.g. {{#each x in list}} ... {{bindAttr attr="x"}} + if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { + view.registerObserver(normalized.root, normalized.path, observer); + } + + // if this changes, also change the logic in ember-views/lib/views/view.js + if ((type === 'string' || (type === 'number' && !isNaN(value)))) { + ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); + } else if (value && type === 'boolean') { + // The developer controls the attr name, so it should always be safe + ret.push(attr + '="' + attr + '"'); + } + }, this); + + // Add the unique identifier + // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG + ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); + return new EmberHandlebars.SafeString(ret.join(' ')); +}); + +/** + @private + + Helper that, given a space-separated string of property paths and a context, + returns an array of class names. Calling this method also has the side + effect of setting up observers at those property paths, such that if they + change, the correct class name will be reapplied to the DOM element. + + For example, if you pass the string "fooBar", it will first look up the + "fooBar" value of the context. If that value is true, it will add the + "foo-bar" class to the current element (i.e., the dasherized form of + "fooBar"). If the value is a string, it will add that string as the class. + Otherwise, it will not add any new class name. + + @method bindClasses + @for Ember.Handlebars + @param {Ember.Object} context The context from which to lookup properties + @param {String} classBindings A string, space-separated, of class bindings + to use + @param {Ember.View} view The view in which observers should look for the + element to update + @param {Srting} bindAttrId Optional bindAttr id used to lookup elements + @return {Array} An array of class names to add +*/ +EmberHandlebars.bindClasses = function(context, classBindings, view, bindAttrId, options) { + var ret = [], newClass, value, elem; + + // Helper method to retrieve the property from the context and + // determine which class string to return, based on whether it is + // a Boolean or not. + var classStringForPath = function(root, parsedPath, options) { + var val, + path = parsedPath.path; + + if (path === 'this') { + val = root; + } else if (path === '') { + val = true; + } else { + val = handlebarsGet(root, path, options); + } + + return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + }; + + // For each property passed, loop through and setup + // an observer. + forEach.call(classBindings.split(' '), function(binding) { + + // Variable in which the old class value is saved. The observer function + // closes over this variable, so it knows which string to remove when + // the property changes. + var oldClass; + + var observer, invoker; + + var parsedPath = Ember.View._parsePropertyPath(binding), + path = parsedPath.path, + pathRoot = context, + normalized; + + if (path !== '' && path !== 'this') { + normalized = normalizePath(context, path, options.data); + + pathRoot = normalized.root; + path = normalized.path; + } + + // Set up an observer on the context. If the property changes, toggle the + // class name. + observer = function() { + // Get the current value of the property + newClass = classStringForPath(context, parsedPath, options); + elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); + + // If we can't find the element anymore, a parent template has been + // re-rendered and we've been nuked. Remove the observer. + if (!elem || elem.length === 0) { + Ember.removeObserver(pathRoot, path, invoker); + } else { + // If we had previously added a class to the element, remove it. + if (oldClass) { + elem.removeClass(oldClass); + } + + // If necessary, add a new class. Make sure we keep track of it so + // it can be removed in the future. + if (newClass) { + elem.addClass(newClass); + oldClass = newClass; + } else { + oldClass = null; + } + } + }; + + if (path !== '' && path !== 'this') { + view.registerObserver(pathRoot, path, observer); + } + + // We've already setup the observer; now we just need to figure out the + // correct behavior right now on the first pass through. + value = classStringForPath(context, parsedPath, options); + + if (value) { + ret.push(value); + + // Make sure we save the current value so that it can be removed if the + // observer fires. + oldClass = value; + } + }); + + return ret; +}; + + +})(); + + + +(function() { +/*globals Handlebars */ + +// TODO: Don't require the entire module +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; +var EmberHandlebars = Ember.Handlebars; + +EmberHandlebars.ViewHelper = Ember.Object.create({ + + propertiesFromHTMLOptions: function(options, thisContext) { + var hash = options.hash, data = options.data; + var extensions = {}, + classes = hash['class'], + dup = false; + + if (hash.id) { + extensions.elementId = hash.id; + dup = true; + } + + if (hash.tag) { + extensions.tagName = hash.tag; + dup = true; + } + + if (classes) { + classes = classes.split(' '); + extensions.classNames = classes; + dup = true; + } + + if (hash.classBinding) { + extensions.classNameBindings = hash.classBinding.split(' '); + dup = true; + } + + if (hash.classNameBindings) { + if (extensions.classNameBindings === undefined) extensions.classNameBindings = []; + extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); + dup = true; + } + + if (hash.attributeBindings) { + Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed. Please subclass Ember.View and set it there instead."); + extensions.attributeBindings = null; + dup = true; + } + + if (dup) { + hash = Ember.$.extend({}, hash); + delete hash.id; + delete hash.tag; + delete hash['class']; + delete hash.classBinding; + } + + // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings + // as well as class name bindings. If the bindings are local, make them relative to the current context + // instead of the view. + var path; + + // Evaluate the context of regular attribute bindings: + for (var prop in hash) { + if (!hash.hasOwnProperty(prop)) { continue; } + + // Test if the property ends in "Binding" + if (Ember.IS_BINDING.test(prop) && typeof hash[prop] === 'string') { + path = this.contextualizeBindingPath(hash[prop], data); + if (path) { hash[prop] = path; } + } + } + + // Evaluate the context of class name bindings: + if (extensions.classNameBindings) { + for (var b in extensions.classNameBindings) { + var full = extensions.classNameBindings[b]; + if (typeof full === 'string') { + // Contextualize the path of classNameBinding so this: + // + // classNameBinding="isGreen:green" + // + // is converted to this: + // + // classNameBinding="_parentView.context.isGreen:green" + var parsedPath = Ember.View._parsePropertyPath(full); + path = this.contextualizeBindingPath(parsedPath.path, data); + if (path) { extensions.classNameBindings[b] = path + parsedPath.classNames; } + } + } + } + + return Ember.$.extend(hash, extensions); + }, + + // Transform bindings from the current context to a context that can be evaluated within the view. + // Returns null if the path shouldn't be changed. + // + // TODO: consider the addition of a prefix that would allow this method to return `path`. + contextualizeBindingPath: function(path, data) { + var normalized = Ember.Handlebars.normalizePath(null, path, data); + if (normalized.isKeyword) { + return 'templateData.keywords.' + path; + } else if (Ember.isGlobalPath(path)) { + return null; + } else if (path === 'this') { + return '_parentView.context'; + } else { + return '_parentView.context.' + path; + } + }, + + helper: function(thisContext, path, options) { + var data = options.data, + fn = options.fn, + newView; + + if ('string' === typeof path) { + newView = EmberHandlebars.get(thisContext, path, options); + Ember.assert("Unable to find view at path '" + path + "'", !!newView); + } else { + newView = path; + } + + Ember.assert(Ember.String.fmt('You must pass a view to the #view helper, not %@ (%@)', [path, newView]), Ember.View.detect(newView) || Ember.View.detectInstance(newView)); + + var viewOptions = this.propertiesFromHTMLOptions(options, thisContext); + var currentView = data.view; + viewOptions.templateData = data; + var newViewProto = newView.proto ? newView.proto() : newView; + + if (fn) { + Ember.assert("You cannot provide a template block if you also specified a templateName", !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName')); + viewOptions.template = fn; + } + + // We only want to override the `_context` computed property if there is + // no specified controller. See View#_context for more information. + if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) { + viewOptions._context = thisContext; + } + + currentView.appendChild(newView, viewOptions); + } +}); + +/** + `{{view}}` inserts a new instance of `Ember.View` into a template passing its + options to the `Ember.View`'s `create` method and using the supplied block as + the view's own template. + + An empty `<body>` and the following template: + + ```handlebars + A span: + {{#view tagName="span"}} + hello. + {{/view}} + ``` + + Will result in HTML structure: + + ```html + <body> + <!-- Note: the handlebars template script + also results in a rendered Ember.View + which is the outer <div> here --> + + <div class="ember-view"> + A span: + <span id="ember1" class="ember-view"> + Hello. + </span> + </div> + </body> + ``` + + ### `parentView` setting + + The `parentView` property of the new `Ember.View` instance created through + `{{view}}` will be set to the `Ember.View` instance of the template where + `{{view}}` was called. + + ```javascript + aView = Ember.View.create({ + template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") + }); + + aView.appendTo('body'); + ``` + + Will result in HTML structure: + + ```html + <div id="ember1" class="ember-view"> + <div id="ember2" class="ember-view"> + my parent: ember1 + </div> + </div> + ``` + + ### Setting CSS id and class attributes + + The HTML `id` attribute can be set on the `{{view}}`'s resulting element with + the `id` option. This option will _not_ be passed to `Ember.View.create`. + + ```handlebars + {{#view tagName="span" id="a-custom-id"}} + hello. + {{/view}} + ``` + + Results in the following HTML structure: + + ```html + <div class="ember-view"> + <span id="a-custom-id" class="ember-view"> + hello. + </span> + </div> + ``` + + The HTML `class` attribute can be set on the `{{view}}`'s resulting element + with the `class` or `classNameBindings` options. The `class` option will + directly set the CSS `class` attribute and will not be passed to + `Ember.View.create`. `classNameBindings` will be passed to `create` and use + `Ember.View`'s class name binding functionality: + + ```handlebars + {{#view tagName="span" class="a-custom-class"}} + hello. + {{/view}} + ``` + + Results in the following HTML structure: + + ```html + <div class="ember-view"> + <span id="ember2" class="ember-view a-custom-class"> + hello. + </span> + </div> + ``` + + ### Supplying a different view class + + `{{view}}` can take an optional first argument before its supplied options to + specify a path to a custom view class. + + ```handlebars + {{#view "MyApp.CustomView"}} + hello. + {{/view}} + ``` + + The first argument can also be a relative path accessible from the current + context. + + ```javascript + MyApp = Ember.Application.create({}); + MyApp.OuterView = Ember.View.extend({ + innerViewClass: Ember.View.extend({ + classNames: ['a-custom-view-class-as-property'] + }), + template: Ember.Handlebars.compile('{{#view "view.innerViewClass"}} hi {{/view}}') + }); + + MyApp.OuterView.create().appendTo('body'); + ``` + + Will result in the following HTML: + + ```html + <div id="ember1" class="ember-view"> + <div id="ember2" class="ember-view a-custom-view-class-as-property"> + hi + </div> + </div> + ``` + + ### Blockless use + + If you supply a custom `Ember.View` subclass that specifies its own template + or provide a `templateName` option to `{{view}}` it can be used without + supplying a block. Attempts to use both a `templateName` option and supply a + block will throw an error. + + ```handlebars + {{view "MyApp.ViewWithATemplateDefined"}} + ``` + + ### `viewName` property + + You can supply a `viewName` option to `{{view}}`. The `Ember.View` instance + will be referenced as a property of its parent view by this name. + + ```javascript + aView = Ember.View.create({ + template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') + }); + + aView.appendTo('body'); + aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper + ``` + + @method view + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options + @return {String} HTML string +*/ +EmberHandlebars.registerHelper('view', function(path, options) { + Ember.assert("The view helper only takes a single argument", arguments.length <= 2); + + // If no path is provided, treat path param as options. + if (path && path.data && path.data.isRenderData) { + options = path; + path = "Ember.View"; + } + + return EmberHandlebars.ViewHelper.helper(this, path, options); +}); + + +})(); + + + +(function() { +/*globals Handlebars */ + +// TODO: Don't require all of this module +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fmt; + +/** + `{{collection}}` is a `Ember.Handlebars` helper for adding instances of + `Ember.CollectionView` to a template. See `Ember.CollectionView` for + additional information on how a `CollectionView` functions. + + `{{collection}}`'s primary use is as a block helper with a `contentBinding` + option pointing towards an `Ember.Array`-compatible object. An `Ember.View` + instance will be created for each item in its `content` property. Each view + will have its own `content` property set to the appropriate item in the + collection. + + The provided block will be applied as the template for each item's view. + + Given an empty `<body>` the following template: + + ```handlebars + {{#collection contentBinding="App.items"}} + Hi {{view.content.name}} + {{/collection}} + ``` + + And the following application code + + ```javascript + App = Ember.Application.create() + App.items = [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ] + ``` + + Will result in the HTML structure below + + ```html + <div class="ember-view"> + <div class="ember-view">Hi Dave</div> + <div class="ember-view">Hi Mary</div> + <div class="ember-view">Hi Sara</div> + </div> + ``` + + ### Blockless Use + + If you provide an `itemViewClass` option that has its own `template` you can + omit the block. + + The following template: + + ```handlebars + {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} + ``` + + And application code + + ```javascript + App = Ember.Application.create(); + App.items = [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ]; + + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{view.content.name}}") + }); + ``` + + Will result in the HTML structure below + + ```html + <div class="ember-view"> + <div class="ember-view">Greetings Dave</div> + <div class="ember-view">Greetings Mary</div> + <div class="ember-view">Greetings Sara</div> + </div> + ``` + + ### Specifying a CollectionView subclass + + By default the `{{collection}}` helper will create an instance of + `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to + the helper by passing it as the first argument: + + ```handlebars + {{#collection App.MyCustomCollectionClass contentBinding="App.items"}} + Hi {{view.content.name}} + {{/collection}} + ``` + + ### Forwarded `item.*`-named Options + + As with the `{{view}}`, helper options passed to the `{{collection}}` will be + set on the resulting `Ember.CollectionView` as properties. Additionally, + options prefixed with `item` will be applied to the views rendered for each + item (note the camelcasing): + + ```handlebars + {{#collection contentBinding="App.items" + itemTagName="p" + itemClassNames="greeting"}} + Howdy {{view.content.name}} + {{/collection}} + ``` + + Will result in the following HTML structure: + + ```html + <div class="ember-view"> + <p class="ember-view greeting">Howdy Dave</p> + <p class="ember-view greeting">Howdy Mary</p> + <p class="ember-view greeting">Howdy Sara</p> + </div> + ``` + + @method collection + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options + @return {String} HTML string + @deprecated Use `{{each}}` helper instead. +*/ +Ember.Handlebars.registerHelper('collection', function(path, options) { + Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); + + // If no path is provided, treat path param as options. + if (path && path.data && path.data.isRenderData) { + options = path; + path = undefined; + Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 1); + } else { + Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2); + } + + var fn = options.fn; + var data = options.data; + var inverse = options.inverse; + var view = options.data.view; + + // If passed a path string, convert that into an object. + // Otherwise, just default to the standard class. + var collectionClass; + collectionClass = path ? handlebarsGet(this, path, options) : Ember.CollectionView; + Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); + + var hash = options.hash, itemHash = {}, match; + + // Extract item view class if provided else default to the standard class + var itemViewClass, itemViewPath = hash.itemViewClass; + var collectionPrototype = collectionClass.proto(); + delete hash.itemViewClass; + itemViewClass = itemViewPath ? handlebarsGet(collectionPrototype, itemViewPath, options) : collectionPrototype.itemViewClass; + Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewPath]), !!itemViewClass); + + // Go through options passed to the {{collection}} helper and extract options + // that configure item views instead of the collection itself. + for (var prop in hash) { + if (hash.hasOwnProperty(prop)) { + match = prop.match(/^item(.)(.*)$/); + + if(match && prop !== 'itemController') { + // Convert itemShouldFoo -> shouldFoo + itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; + // Delete from hash as this will end up getting passed to the + // {{view}} helper method. + delete hash[prop]; + } + } + } + + if (fn) { + itemHash.template = fn; + delete options.fn; + } + + var emptyViewClass; + if (inverse && inverse !== Handlebars.VM.noop) { + emptyViewClass = get(collectionPrototype, 'emptyViewClass'); + emptyViewClass = emptyViewClass.extend({ + template: inverse, + tagName: itemHash.tagName + }); + } else if (hash.emptyViewClass) { + emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); + } + if (emptyViewClass) { hash.emptyView = emptyViewClass; } + + if(!hash.keyword){ + itemHash._context = Ember.computed.alias('content'); + } + + var viewOptions = Ember.Handlebars.ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); + hash.itemViewClass = itemViewClass.extend(viewOptions); + + return Ember.Handlebars.helpers.view.call(this, collectionClass, options); +}); + + +})(); + + + +(function() { +/*globals Handlebars */ +/** +@module ember +@submodule ember-handlebars +*/ + +var handlebarsGet = Ember.Handlebars.get; + +/** + `unbound` allows you to output a property without binding. *Important:* The + output will not be updated if the property changes. Use with caution. + + ```handlebars + <div>{{unbound somePropertyThatDoesntChange}}</div> + ``` + + `unbound` can also be used in conjunction with a bound helper to + render it in its unbound form: + + ```handlebars + <div>{{unbound helperName somePropertyThatDoesntChange}}</div> + ``` + + @method unbound + @for Ember.Handlebars.helpers + @param {String} property + @return {String} HTML string +*/ +Ember.Handlebars.registerHelper('unbound', function(property, fn) { + var options = arguments[arguments.length - 1], helper, context, out; + + if(arguments.length > 2) { + // Unbound helper call. + options.data.isUnbound = true; + helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helperMissing; + out = helper.apply(this, Array.prototype.slice.call(arguments, 1)); + delete options.data.isUnbound; + return out; + } + + context = (fn.contexts && fn.contexts[0]) || this; + return handlebarsGet(context, property, fn); +}); + +})(); + + + +(function() { +/*jshint debug:true*/ +/** +@module ember +@submodule ember-handlebars +*/ + +var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; + +/** + `log` allows you to output the value of a value in the current rendering + context. + + ```handlebars + {{log myVariable}} + ``` + + @method log + @for Ember.Handlebars.helpers + @param {String} property +*/ +Ember.Handlebars.registerHelper('log', function(property, options) { + var context = (options.contexts && options.contexts[0]) || this, + normalized = normalizePath(context, property, options.data), + pathRoot = normalized.root, + path = normalized.path, + value = (path === 'this') ? pathRoot : handlebarsGet(pathRoot, path, options); + Ember.Logger.log(value); +}); + +/** + Execute the `debugger` statement in the current context. + + ```handlebars + {{debugger}} + ``` + + @method debugger + @for Ember.Handlebars.helpers + @param {String} property +*/ +Ember.Handlebars.registerHelper('debugger', function() { + debugger; +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, { + init: function() { + var itemController = get(this, 'itemController'); + var binding; + + if (itemController) { + var controller = Ember.ArrayController.create(); + set(controller, 'itemController', itemController); + set(controller, 'container', get(this, 'controller.container')); + set(controller, '_eachView', this); + set(controller, 'target', get(this, 'controller')); + set(controller, 'parentController', get(this, 'controller')); + + this.disableContentObservers(function() { + set(this, 'content', controller); + binding = new Ember.Binding('content', '_eachView.dataSource').oneWay(); + binding.connect(controller); + }); + + set(this, '_arrayController', controller); + } else { + this.disableContentObservers(function() { + binding = new Ember.Binding('content', 'dataSource').oneWay(); + binding.connect(this); + }); + } + + return this._super(); + }, + + disableContentObservers: function(callback) { + Ember.removeBeforeObserver(this, 'content', null, '_contentWillChange'); + Ember.removeObserver(this, 'content', null, '_contentDidChange'); + + callback.call(this); + + Ember.addBeforeObserver(this, 'content', null, '_contentWillChange'); + Ember.addObserver(this, 'content', null, '_contentDidChange'); + }, + + itemViewClass: Ember._MetamorphView, + emptyViewClass: Ember._MetamorphView, + + createChildView: function(view, attrs) { + view = this._super(view, attrs); + + // At the moment, if a container view subclass wants + // to insert keywords, it is responsible for cloning + // the keywords hash. This will be fixed momentarily. + var keyword = get(this, 'keyword'); + var content = get(view, 'content'); + + if (keyword) { + var data = get(view, 'templateData'); + + data = Ember.copy(data); + data.keywords = view.cloneKeywords(); + set(view, 'templateData', data); + + // In this case, we do not bind, because the `content` of + // a #each item cannot change. + data.keywords[keyword] = content; + } + + // If {{#each}} is looping over an array of controllers, + // point each child view at their respective controller. + if (content && get(content, 'isController')) { + set(view, 'controller', content); + } + + return view; + }, + + destroy: function() { + if (!this._super()) { return; } + + var arrayController = get(this, '_arrayController'); + + if (arrayController) { + arrayController.destroy(); + } + + return this; + } +}); + +var GroupedEach = Ember.Handlebars.GroupedEach = function(context, path, options) { + var self = this, + normalized = Ember.Handlebars.normalizePath(context, path, options.data); + + this.context = context; + this.path = path; + this.options = options; + this.template = options.fn; + this.containingView = options.data.view; + this.normalizedRoot = normalized.root; + this.normalizedPath = normalized.path; + this.content = this.lookupContent(); + + this.addContentObservers(); + this.addArrayObservers(); + + this.containingView.on('willClearRender', function() { + self.destroy(); + }); +}; + +GroupedEach.prototype = { + contentWillChange: function() { + this.removeArrayObservers(); + }, + + contentDidChange: function() { + this.content = this.lookupContent(); + this.addArrayObservers(); + this.rerenderContainingView(); + }, + + contentArrayWillChange: Ember.K, + + contentArrayDidChange: function() { + this.rerenderContainingView(); + }, + + lookupContent: function() { + return Ember.Handlebars.get(this.normalizedRoot, this.normalizedPath, this.options); + }, + + addArrayObservers: function() { + this.content.addArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + }, + + removeArrayObservers: function() { + this.content.removeArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + }, + + addContentObservers: function() { + Ember.addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); + Ember.addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); + }, + + removeContentObservers: function() { + Ember.removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); + Ember.removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); + }, + + render: function() { + var content = this.content, + contentLength = get(content, 'length'), + data = this.options.data, + template = this.template; + + data.insideEach = true; + for (var i = 0; i < contentLength; i++) { + template(content.objectAt(i), { data: data }); + } + }, + + rerenderContainingView: function() { + Ember.run.scheduleOnce('render', this.containingView, 'rerender'); + }, + + destroy: function() { + this.removeContentObservers(); + this.removeArrayObservers(); + } +}; + +/** + The `{{#each}}` helper loops over elements in a collection, rendering its + block once for each item. It is an extension of the base Handlebars `{{#each}}` + helper: + + ```javascript + Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; + ``` + + ```handlebars + {{#each Developers}} + {{name}} + {{/each}} + ``` + + `{{each}}` supports an alternative syntax with element naming: + + ```handlebars + {{#each person in Developers}} + {{person.name}} + {{/each}} + ``` + + When looping over objects that do not have properties, `{{this}}` can be used + to render the object: + + ```javascript + DeveloperNames = ['Yehuda', 'Tom', 'Paul'] + ``` + + ```handlebars + {{#each DeveloperNames}} + {{this}} + {{/each}} + ``` + ### {{else}} condition + `{{#each}}` can have a matching `{{else}}`. The contents of this block will render + if the collection is empty. + + ``` + {{#each person in Developers}} + {{person.name}} + {{else}} + <p>Sorry, nobody is available for this task.</p> + {{/each}} + ``` + ### Specifying a View class for items + If you provide an `itemViewClass` option that references a view class + with its own `template` you can omit the block. + + The following template: + + ```handlebars + {{#view App.MyView }} + {{each view.items itemViewClass="App.AnItemView"}} + {{/view}} + ``` + + And application code + + ```javascript + App = Ember.Application.create({ + MyView: Ember.View.extend({ + items: [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ] + }) + }); + + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{name}}") + }); + ``` + + Will result in the HTML structure below + + ```html + <div class="ember-view"> + <div class="ember-view">Greetings Dave</div> + <div class="ember-view">Greetings Mary</div> + <div class="ember-view">Greetings Sara</div> + </div> + ``` + + ### Representing each item with a Controller. + By default the controller lookup within an `{{#each}}` block will be + the controller of the template where the `{{#each}}` was used. If each + item needs to be presented by a custom controller you can provide a + `itemController` option which references a controller by lookup name. + Each item in the loop will be wrapped in an instance of this controller + and the item itself will be set to the `content` property of that controller. + + This is useful in cases where properties of model objects need transformation + or synthesis for display: + + ```javascript + App.DeveloperController = Ember.ObjectController.extend({ + isAvailableForHire: function(){ + return !this.get('content.isEmployed') && this.get('content.isSeekingWork'); + }.property('isEmployed', 'isSeekingWork') + }) + ``` + + ```handlebars + {{#each person in developers itemController="developer"}} + {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} + {{/each}} + ``` + + Each itemController will receive a reference to the current controller as + a `parentController` property. + + @method each + @for Ember.Handlebars.helpers + @param [name] {String} name for item (used with `in`) + @param [path] {String} path + @param [options] {Object} Handlebars key/value pairs of options + @param [options.itemViewClass] {String} a path to a view class used for each item + @param [options.itemController] {String} name of a controller to be created for each item +*/ +Ember.Handlebars.registerHelper('each', function(path, options) { + if (arguments.length === 4) { + Ember.assert("If you pass more than one argument to the each helper, it must be in the form #each foo in bar", arguments[1] === "in"); + + var keywordName = arguments[0]; + + options = arguments[3]; + path = arguments[2]; + if (path === '') { path = "this"; } + + options.hash.keyword = keywordName; + } + + if (arguments.length === 1) { + options = path; + path = 'this'; + } + + options.hash.dataSourceBinding = path; + // Set up emptyView as a metamorph with no tag + //options.hash.emptyViewClass = Ember._MetamorphView; + + if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { + new Ember.Handlebars.GroupedEach(this, path, options).render(); + } else { + return Ember.Handlebars.helpers.collection.call(this, 'Ember.Handlebars.EachView', options); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +/** + `template` allows you to render a template from inside another template. + This allows you to re-use the same template in multiple places. For example: + + ```html + <script type="text/x-handlebars" data-template-name="logged_in_user"> + {{#with loggedInUser}} + Last Login: {{lastLogin}} + User Info: {{template "user_info"}} + {{/with}} + </script> + ``` + + ```html + <script type="text/x-handlebars" data-template-name="user_info"> + Name: <em>{{name}}</em> + Karma: <em>{{karma}}</em> + </script> + ``` + + ```handlebars + {{#if isUser}} + {{template "user_info"}} + {{else}} + {{template "unlogged_user_info"}} + {{/if}} + ``` + + This helper looks for templates in the global `Ember.TEMPLATES` hash. If you + add `<script>` tags to your page with the `data-template-name` attribute set, + they will be compiled and placed in this hash automatically. + + You can also manually register templates by adding them to the hash: + + ```javascript + Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>'); + ``` + + @method template + @for Ember.Handlebars.helpers + @param {String} templateName the template to render +*/ + +Ember.Handlebars.registerHelper('template', function(name, options) { + var view = options.data.view, + template = view.templateForName(name); + + Ember.assert("Unable to find template with name '"+name+"'.", !!template); + + template(this, { data: options.data }); +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +/** + `partial` renders a template directly using the current context. + If needed the context can be set using the `{{#with foo}}` helper. + + ```html + <script type="text/x-handlebars" data-template-name="header_bar"> + {{#with currentUser}} + {{partial user_info}} + {{/with}} + </script> + ``` + + The `data-template-name` attribute of a partial template + is prefixed with an underscore. + + ```html + <script type="text/x-handlebars" data-template-name="_user_info"> + <span>Hello {{username}}!</span> + </script> + ``` + + @method partial + @for Ember.Handlebars.helpers + @param {String} partialName the name of the template to render minus the leading underscore +*/ + +Ember.Handlebars.registerHelper('partial', function(name, options) { + var nameParts = name.split("/"), + lastPart = nameParts[nameParts.length - 1]; + + nameParts[nameParts.length - 1] = "_" + lastPart; + + var view = options.data.view, + underscoredName = nameParts.join("/"), + template = view.templateForName(underscoredName), + deprecatedTemplate = !template && view.templateForName(name); + + Ember.deprecate("You tried to render the partial " + name + ", which should be at '" + underscoredName + "', but Ember found '" + name + "'. Please use a leading underscore in your partials", template); + Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); + + template = template || deprecatedTemplate; + + template(this, { data: options.data }); +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/** + When used in a Handlebars template that is assigned to an `Ember.View` + instance's `layout` property Ember will render the layout template first, + inserting the view's own rendered output at the `{{yield}}` location. + + An empty `<body>` and the following application code: + + ```javascript + AView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + layout: Ember.Handlebars.compile('<div class="wrapper">{{yield}}</div>'), + template: Ember.Handlebars.compile('<span>I am wrapped</span>') + }); + + aView = AView.create(); + aView.appendTo('body'); + ``` + + Will result in the following HTML output: + + ```html + <body> + <div class='ember-view a-view-with-layout'> + <div class="wrapper"> + <span>I am wrapped</span> + </div> + </div> + </body> + ``` + + The `yield` helper cannot be used outside of a template assigned to an + `Ember.View`'s `layout` property and will throw an error if attempted. + + ```javascript + BView = Ember.View.extend({ + classNames: ['a-view-with-layout'], + template: Ember.Handlebars.compile('{{yield}}') + }); + + bView = BView.create(); + bView.appendTo('body'); + + // throws + // Uncaught Error: assertion failed: You called yield in a template that was not a layout + ``` + + @method yield + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string +*/ +Ember.Handlebars.registerHelper('yield', function(options) { + var view = options.data.view, template; + + while (view && !get(view, 'layout')) { + view = get(view, 'parentView'); + } + + Ember.assert("You called yield in a template that was not a layout", !!view); + + template = get(view, 'template'); + + if (template) { template(this, options); } +}); + +})(); + + + +(function() { + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var set = Ember.set, get = Ember.get; + +/** + The `Ember.Checkbox` view class renders a checkbox + [input](https://developer.mozilla.org/en/HTML/Element/Input) element. It + allows for binding an Ember property (`checked`) to the status of the + checkbox. + + Example: + + ```handlebars + {{view Ember.Checkbox checkedBinding="receiveEmail"}} + ``` + + You can add a `label` tag yourself in the template where the `Ember.Checkbox` + is being used. + + ```handlebars + <label> + {{view Ember.Checkbox classNames="applicaton-specific-checkbox"}} + Some Title + </label> + ``` + + The `checked` attribute of an `Ember.Checkbox` object should always be set + through the Ember object or by interacting with its rendered element + representation via the mouse, keyboard, or touch. Updating the value of the + checkbox via jQuery will result in the checked value of the object and its + element losing synchronization. + + ## Layout and LayoutName properties + + Because HTML `input` elements are self closing `layout` and `layoutName` + properties will not be applied. See `Ember.View`'s layout section for more + information. + + @class Checkbox + @namespace Ember + @extends Ember.View +*/ +Ember.Checkbox = Ember.View.extend({ + classNames: ['ember-checkbox'], + + tagName: 'input', + + attributeBindings: ['type', 'checked', 'disabled', 'tabindex', 'name'], + + type: "checkbox", + checked: false, + disabled: false, + + init: function() { + this._super(); + this.on("change", this, this._updateElementValue); + }, + + _updateElementValue: function() { + set(this, 'checked', this.$().prop('checked')); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/** + Shared mixin used by `Ember.TextField` and `Ember.TextArea`. + + @class TextSupport + @namespace Ember + @extends Ember.Mixin + @private +*/ +Ember.TextSupport = Ember.Mixin.create({ + value: "", + + attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex'], + placeholder: null, + disabled: false, + maxlength: null, + + insertNewline: Ember.K, + cancel: Ember.K, + + init: function() { + this._super(); + this.on("focusOut", this, this._elementValueDidChange); + this.on("change", this, this._elementValueDidChange); + this.on("paste", this, this._elementValueDidChange); + this.on("cut", this, this._elementValueDidChange); + this.on("input", this, this._elementValueDidChange); + this.on("keyUp", this, this.interpretKeyEvents); + }, + + interpretKeyEvents: function(event) { + var map = Ember.TextSupport.KEY_EVENTS; + var method = map[event.keyCode]; + + this._elementValueDidChange(); + if (method) { return this[method](event); } + }, + + _elementValueDidChange: function() { + set(this, 'value', this.$().val()); + } + +}); + +Ember.TextSupport.KEY_EVENTS = { + 13: 'insertNewline', + 27: 'cancel' +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/** + The `Ember.TextField` view class renders a text + [input](https://developer.mozilla.org/en/HTML/Element/Input) element. It + allows for binding Ember properties to the text field contents (`value`), + live-updating as the user inputs text. + + Example: + + ```handlebars + {{view Ember.TextField valueBinding="firstName"}} + ``` + + ## Layout and LayoutName properties + + Because HTML `input` elements are self closing `layout` and `layoutName` + properties will not be applied. See `Ember.View`'s layout section for more + information. + + ## HTML Attributes + + By default `Ember.TextField` provides support for `type`, `value`, `size`, + `pattern`, `placeholder`, `disabled`, `maxlength` and `tabindex` attributes + on a text field. If you need to support more attributes have a look at the + `attributeBindings` property in `Ember.View`'s HTML Attributes section. + + To globally add support for additional attributes you can reopen + `Ember.TextField` or `Ember.TextSupport`. + + ```javascript + Ember.TextSupport.reopen({ + attributeBindings: ["required"] + }) + ``` + + @class TextField + @namespace Ember + @extends Ember.View + @uses Ember.TextSupport +*/ +Ember.TextField = Ember.View.extend(Ember.TextSupport, + /** @scope Ember.TextField.prototype */ { + + classNames: ['ember-text-field'], + tagName: "input", + attributeBindings: ['type', 'value', 'size', 'pattern', 'name'], + + /** + The `value` attribute of the input element. As the user inputs text, this + property is updated live. + + @property value + @type String + @default "" + */ + value: "", + + /** + The `type` attribute of the input element. + + @property type + @type String + @default "text" + */ + type: "text", + + /** + The `size` of the text field in characters. + + @property size + @type String + @default null + */ + size: null, + + /** + The `pattern` the pattern attribute of input element. + + @property pattern + @type String + @default null + */ + pattern: null, + + /** + The action to be sent when the user presses the return key. + + This is similar to the `{{action}}` helper, but is fired when + the user presses the return key when editing a text field, and sends + the value of the field as the context. + + @property action + @type String + @default null + */ + action: null, + + /** + The event that should send the action. + + Options are: + + * `enter`: the user pressed enter + * `keypress`: the user pressed a key + + @property onEvent + @type String + @default enter + */ + onEvent: 'enter', + + /** + Whether they `keyUp` event that triggers an `action` to be sent continues + propagating to other views. + + By default, when the user presses the return key on their keyboard and + the text field has an `action` set, the action will be sent to the view's + controller and the key event will stop propagating. + + If you would like parent views to receive the `keyUp` event even after an + action has been dispatched, set `bubbles` to true. + + @property bubbles + @type Boolean + @default false + */ + bubbles: false, + + insertNewline: function(event) { + sendAction('enter', this, event); + }, + + keyPress: function(event) { + sendAction('keyPress', this, event); + } +}); + +function sendAction(eventName, view, event) { + var action = get(view, 'action'), + on = get(view, 'onEvent'); + + if (action && on === eventName) { + var controller = get(view, 'controller'), + value = get(view, 'value'), + bubbles = get(view, 'bubbles'); + + controller.send(action, value, view); + + if (!bubbles) { + event.stopPropagation(); + } + } +} +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/** + @class Button + @namespace Ember + @extends Ember.View + @uses Ember.TargetActionSupport + @deprecated +*/ +Ember.Button = Ember.View.extend(Ember.TargetActionSupport, { + classNames: ['ember-button'], + classNameBindings: ['isActive'], + + tagName: 'button', + + propagateEvents: false, + + attributeBindings: ['type', 'disabled', 'href', 'tabindex'], + + /** + @private + + Overrides `TargetActionSupport`'s `targetObject` computed + property to use Handlebars-specific path resolution. + + @property targetObject + */ + targetObject: Ember.computed(function() { + var target = get(this, 'target'), + root = get(this, 'context'), + data = get(this, 'templateData'); + + if (typeof target !== 'string') { return target; } + + return Ember.Handlebars.get(root, target, { data: data }); + }).property('target'), + + // Defaults to 'button' if tagName is 'input' or 'button' + type: Ember.computed(function(key) { + var tagName = this.tagName; + if (tagName === 'input' || tagName === 'button') { return 'button'; } + }), + + disabled: false, + + // Allow 'a' tags to act like buttons + href: Ember.computed(function() { + return this.tagName === 'a' ? '#' : null; + }), + + mouseDown: function() { + if (!get(this, 'disabled')) { + set(this, 'isActive', true); + this._mouseDown = true; + this._mouseEntered = true; + } + return get(this, 'propagateEvents'); + }, + + mouseLeave: function() { + if (this._mouseDown) { + set(this, 'isActive', false); + this._mouseEntered = false; + } + }, + + mouseEnter: function() { + if (this._mouseDown) { + set(this, 'isActive', true); + this._mouseEntered = true; + } + }, + + mouseUp: function(event) { + if (get(this, 'isActive')) { + // Actually invoke the button's target and action. + // This method comes from the Ember.TargetActionSupport mixin. + this.triggerAction(); + set(this, 'isActive', false); + } + + this._mouseDown = false; + this._mouseEntered = false; + return get(this, 'propagateEvents'); + }, + + keyDown: function(event) { + // Handle space or enter + if (event.keyCode === 13 || event.keyCode === 32) { + this.mouseDown(); + } + }, + + keyUp: function(event) { + // Handle space or enter + if (event.keyCode === 13 || event.keyCode === 32) { + this.mouseUp(); + } + }, + + // TODO: Handle proper touch behavior. Including should make inactive when + // finger moves more than 20x outside of the edge of the button (vs mouse + // which goes inactive as soon as mouse goes out of edges.) + + touchStart: function(touch) { + return this.mouseDown(touch); + }, + + touchEnd: function(touch) { + return this.mouseUp(touch); + }, + + init: function() { + Ember.deprecate("Ember.Button is deprecated and will be removed from future releases. Consider using the `{{action}}` helper."); + this._super(); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/** + The `Ember.TextArea` view class renders a + [textarea](https://developer.mozilla.org/en/HTML/Element/textarea) element. + It allows for binding Ember properties to the text area contents (`value`), + live-updating as the user inputs text. + + ## Layout and LayoutName properties + + Because HTML `textarea` elements do not contain inner HTML the `layout` and + `layoutName` properties will not be applied. See `Ember.View`'s layout + section for more information. + + ## HTML Attributes + + By default `Ember.TextArea` provides support for `rows`, `cols`, + `placeholder`, `disabled`, `maxlength` and `tabindex` attributes on a + textarea. If you need to support more attributes have a look at the + `attributeBindings` property in `Ember.View`'s HTML Attributes section. + + To globally add support for additional attributes you can reopen + `Ember.TextArea` or `Ember.TextSupport`. + + ```javascript + Ember.TextSupport.reopen({ + attributeBindings: ["required"] + }) + ``` + + @class TextArea + @namespace Ember + @extends Ember.View + @uses Ember.TextSupport +*/ +Ember.TextArea = Ember.View.extend(Ember.TextSupport, { + classNames: ['ember-text-area'], + + tagName: "textarea", + attributeBindings: ['rows', 'cols', 'name'], + rows: null, + cols: null, + + _updateElementValue: Ember.observer(function() { + // We do this check so cursor position doesn't get affected in IE + var value = get(this, 'value'), + $el = this.$(); + if ($el && value !== $el.val()) { + $el.val(value); + } + }, 'value'), + + init: function() { + this._super(); + this.on("didInsertElement", this, this._updateElementValue); + } + +}); + +})(); + + + +(function() { +/*jshint eqeqeq:false */ + +/** +@module ember +@submodule ember-handlebars +*/ + +var set = Ember.set, + get = Ember.get, + indexOf = Ember.EnumerableUtils.indexOf, + indexesOf = Ember.EnumerableUtils.indexesOf, + replace = Ember.EnumerableUtils.replace, + isArray = Ember.isArray, + precompileTemplate = Ember.Handlebars.compile; + +Ember.SelectOption = Ember.View.extend({ + tagName: 'option', + attributeBindings: ['value', 'selected'], + + defaultTemplate: function(context, options) { + options = { data: options.data, hash: {} }; + Ember.Handlebars.helpers.bind.call(context, "view.label", options); + }, + + init: function() { + this.labelPathDidChange(); + this.valuePathDidChange(); + + this._super(); + }, + + selected: Ember.computed(function() { + var content = get(this, 'content'), + selection = get(this, 'parentView.selection'); + if (get(this, 'parentView.multiple')) { + return selection && indexOf(selection, content.valueOf()) > -1; + } else { + // Primitives get passed through bindings as objects... since + // `new Number(4) !== 4`, we use `==` below + return content == selection; + } + }).property('content', 'parentView.selection'), + + labelPathDidChange: Ember.observer(function() { + var labelPath = get(this, 'parentView.optionLabelPath'); + + if (!labelPath) { return; } + + Ember.defineProperty(this, 'label', Ember.computed(function() { + return get(this, labelPath); + }).property(labelPath)); + }, 'parentView.optionLabelPath'), + + valuePathDidChange: Ember.observer(function() { + var valuePath = get(this, 'parentView.optionValuePath'); + + if (!valuePath) { return; } + + Ember.defineProperty(this, 'value', Ember.computed(function() { + return get(this, valuePath); + }).property(valuePath)); + }, 'parentView.optionValuePath') +}); + +/** + The `Ember.Select` view class renders a + [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element, + allowing the user to choose from a list of options. + + The text and `value` property of each `<option>` element within the + `<select>` element are populated from the objects in the `Element.Select`'s + `content` property. The underlying data object of the selected `<option>` is + stored in the `Element.Select`'s `value` property. + + ### `content` as an array of Strings + + The simplest version of an `Ember.Select` takes an array of strings as its + `content` property. The string will be used as both the `value` property and + the inner text of each `<option>` element inside the rendered `<select>`. + + Example: + + ```javascript + App.names = ["Yehuda", "Tom"]; + ``` + + ```handlebars + {{view Ember.Select contentBinding="App.names"}} + ``` + + Would result in the following HTML: + + ```html + <select class="ember-select"> + <option value="Yehuda">Yehuda</option> + <option value="Tom">Tom</option> + </select> + ``` + + You can control which `<option>` is selected through the `Ember.Select`'s + `value` property directly or as a binding: + + ```javascript + App.names = Ember.Object.create({ + selected: 'Tom', + content: ["Yehuda", "Tom"] + }); + ``` + + ```handlebars + {{view Ember.Select + contentBinding="App.names.content" + valueBinding="App.names.selected" + }} + ``` + + Would result in the following HTML with the `<option>` for 'Tom' selected: + + ```html + <select class="ember-select"> + <option value="Yehuda">Yehuda</option> + <option value="Tom" selected="selected">Tom</option> + </select> + ``` + + A user interacting with the rendered `<select>` to choose "Yehuda" would + update the value of `App.names.selected` to "Yehuda". + + ### `content` as an Array of Objects + + An `Ember.Select` can also take an array of JavaScript or Ember objects as + its `content` property. + + When using objects you need to tell the `Ember.Select` which property should + be accessed on each object to supply the `value` attribute of the `<option>` + and which property should be used to supply the element text. + + The `optionValuePath` option is used to specify the path on each object to + the desired property for the `value` attribute. The `optionLabelPath` + specifies the path on each object to the desired property for the + element's text. Both paths must reference each object itself as `content`: + + ```javascript + App.programmers = [ + Ember.Object.create({firstName: "Yehuda", id: 1}), + Ember.Object.create({firstName: "Tom", id: 2}) + ]; + ``` + + ```handlebars + {{view Ember.Select + contentBinding="App.programmers" + optionValuePath="content.id" + optionLabelPath="content.firstName"}} + ``` + + Would result in the following HTML: + + ```html + <select class="ember-select"> + <option value="1">Yehuda</option> + <option value="2">Tom</option> + </select> + ``` + + The `value` attribute of the selected `<option>` within an `Ember.Select` + can be bound to a property on another object by providing a + `valueBinding` option: + + ```javascript + App.programmers = [ + Ember.Object.create({firstName: "Yehuda", id: 1}), + Ember.Object.create({firstName: "Tom", id: 2}) + ]; + + App.currentProgrammer = Ember.Object.create({ + id: 2 + }); + ``` + + ```handlebars + {{view Ember.Select + contentBinding="App.programmers" + optionValuePath="content.id" + optionLabelPath="content.firstName" + valueBinding="App.currentProgrammer.id"}} + ``` + + Would result in the following HTML with a selected option: + + ```html + <select class="ember-select"> + <option value="1">Yehuda</option> + <option value="2" selected="selected">Tom</option> + </select> + ``` + + Interacting with the rendered element by selecting the first option + ('Yehuda') will update the `id` value of `App.currentProgrammer` + to match the `value` property of the newly selected `<option>`. + + Alternatively, you can control selection through the underlying objects + used to render each object providing a `selectionBinding`. When the selected + `<option>` is changed, the property path provided to `selectionBinding` + will be updated to match the content object of the rendered `<option>` + element: + + ```javascript + App.controller = Ember.Object.create({ + selectedPerson: null, + content: [ + Ember.Object.create({firstName: "Yehuda", id: 1}), + Ember.Object.create({firstName: "Tom", id: 2}) + ] + }); + ``` + + ```handlebars + {{view Ember.Select + contentBinding="App.controller.content" + optionValuePath="content.id" + optionLabelPath="content.firstName" + selectionBinding="App.controller.selectedPerson"}} + ``` + + Would result in the following HTML with a selected option: + + ```html + <select class="ember-select"> + <option value="1">Yehuda</option> + <option value="2" selected="selected">Tom</option> + </select> + ``` + + Interacting with the rendered element by selecting the first option + ('Yehuda') will update the `selectedPerson` value of `App.controller` + to match the content object of the newly selected `<option>`. In this + case it is the first object in the `App.controller.content` + + ### Supplying a Prompt + + A `null` value for the `Ember.Select`'s `value` or `selection` property + results in there being no `<option>` with a `selected` attribute: + + ```javascript + App.controller = Ember.Object.create({ + selected: null, + content: [ + "Yehuda", + "Tom" + ] + }); + ``` + + ``` handlebars + {{view Ember.Select + contentBinding="App.controller.content" + valueBinding="App.controller.selected" + }} + ``` + + Would result in the following HTML: + + ```html + <select class="ember-select"> + <option value="Yehuda">Yehuda</option> + <option value="Tom">Tom</option> + </select> + ``` + + Although `App.controller.selected` is `null` and no `<option>` + has a `selected` attribute the rendered HTML will display the + first item as though it were selected. You can supply a string + value for the `Ember.Select` to display when there is no selection + with the `prompt` option: + + ```javascript + App.controller = Ember.Object.create({ + selected: null, + content: [ + "Yehuda", + "Tom" + ] + }); + ``` + + ```handlebars + {{view Ember.Select + contentBinding="App.controller.content" + valueBinding="App.controller.selected" + prompt="Please select a name" + }} + ``` + + Would result in the following HTML: + + ```html + <select class="ember-select"> + <option>Please select a name</option> + <option value="Yehuda">Yehuda</option> + <option value="Tom">Tom</option> + </select> + ``` + + @class Select + @namespace Ember + @extends Ember.View +*/ +Ember.Select = Ember.View.extend( + /** @scope Ember.Select.prototype */ { + + tagName: 'select', + classNames: ['ember-select'], + defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) { +this.compilerInfo = [3,'>= 1.0.0-rc.4']; +helpers = helpers || Ember.Handlebars.helpers; data = data || {}; + var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this; + +function program1(depth0,data) { + + var buffer = '', hashTypes, hashContexts; + data.buffer.push("<option value=\"\">"); + hashTypes = {}; + hashContexts = {}; + data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}))); + data.buffer.push("</option>"); + return buffer; + } + +function program3(depth0,data) { + + var hashContexts, hashTypes; + hashContexts = {'contentBinding': depth0}; + hashTypes = {'contentBinding': "STRING"}; + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ + 'contentBinding': ("this") + },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}))); + } + + hashTypes = {}; + hashContexts = {}; + stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + hashTypes = {}; + hashContexts = {}; + stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + return buffer; + +}), + attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'], + + /** + The `multiple` attribute of the select element. Indicates whether multiple + options can be selected. + + @property multiple + @type Boolean + @default false + */ + multiple: false, + + disabled: false, + + /** + The list of options. + + If `optionLabelPath` and `optionValuePath` are not overridden, this should + be a list of strings, which will serve simultaneously as labels and values. + + Otherwise, this should be a list of objects. For instance: + + ```javascript + Ember.Select.create({ + content: Ember.A([ + { id: 1, firstName: 'Yehuda' }, + { id: 2, firstName: 'Tom' } + ]), + optionLabelPath: 'content.firstName', + optionValuePath: 'content.id' + }); + ``` + + @property content + @type Array + @default null + */ + content: null, + + /** + When `multiple` is `false`, the element of `content` that is currently + selected, if any. + + When `multiple` is `true`, an array of such elements. + + @property selection + @type Object or Array + @default null + */ + selection: null, + + /** + In single selection mode (when `multiple` is `false`), value can be used to + get the current selection's value or set the selection by it's value. + + It is not currently supported in multiple selection mode. + + @property value + @type String + @default null + */ + value: Ember.computed(function(key, value) { + if (arguments.length === 2) { return value; } + var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); + return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); + }).property('selection'), + + /** + If given, a top-most dummy option will be rendered to serve as a user + prompt. + + @property prompt + @type String + @default null + */ + prompt: null, + + /** + The path of the option labels. See `content`. + + @property optionLabelPath + @type String + @default 'content' + */ + optionLabelPath: 'content', + + /** + The path of the option values. See `content`. + + @property optionValuePath + @type String + @default 'content' + */ + optionValuePath: 'content', + + /** + The view class for option. + + @property optionView + @type Ember.View + @default Ember.SelectOption + */ + optionView: Ember.SelectOption, + + _change: function() { + if (get(this, 'multiple')) { + this._changeMultiple(); + } else { + this._changeSingle(); + } + }, + + selectionDidChange: Ember.observer(function() { + var selection = get(this, 'selection'); + if (get(this, 'multiple')) { + if (!isArray(selection)) { + set(this, 'selection', Ember.A([selection])); + return; + } + this._selectionDidChangeMultiple(); + } else { + this._selectionDidChangeSingle(); + } + }, 'selection.@each'), + + valueDidChange: Ember.observer(function() { + var content = get(this, 'content'), + value = get(this, 'value'), + valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), + selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), + selection; + + if (value !== selectedValue) { + selection = content ? content.find(function(obj) { + return value === (valuePath ? get(obj, valuePath) : obj); + }) : null; + + this.set('selection', selection); + } + }, 'value'), + + + _triggerChange: function() { + var selection = get(this, 'selection'); + var value = get(this, 'value'); + + if (!Ember.isNone(selection)) { this.selectionDidChange(); } + if (!Ember.isNone(value)) { this.valueDidChange(); } + + this._change(); + }, + + _changeSingle: function() { + var selectedIndex = this.$()[0].selectedIndex, + content = get(this, 'content'), + prompt = get(this, 'prompt'); + + if (!content || !get(content, 'length')) { return; } + if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } + + if (prompt) { selectedIndex -= 1; } + set(this, 'selection', content.objectAt(selectedIndex)); + }, + + + _changeMultiple: function() { + var options = this.$('option:selected'), + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + content = get(this, 'content'), + selection = get(this, 'selection'); + + if (!content){ return; } + if (options) { + var selectedIndexes = options.map(function(){ + return this.index - offset; + }).toArray(); + var newSelection = content.objectsAt(selectedIndexes); + + if (isArray(selection)) { + replace(selection, 0, get(selection, 'length'), newSelection); + } else { + set(this, 'selection', newSelection); + } + } + }, + + _selectionDidChangeSingle: function() { + var el = this.get('element'); + if (!el) { return; } + + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectionIndex = content ? indexOf(content, selection) : -1, + prompt = get(this, 'prompt'); + + if (prompt) { selectionIndex += 1; } + if (el) { el.selectedIndex = selectionIndex; } + }, + + _selectionDidChangeMultiple: function() { + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectedIndexes = content ? indexesOf(content, selection) : [-1], + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + options = this.$('option'), + adjusted; + + if (options) { + options.each(function() { + adjusted = this.index > -1 ? this.index - offset : -1; + this.selected = indexOf(selectedIndexes, adjusted) > -1; + }); + } + }, + + init: function() { + this._super(); + this.on("didInsertElement", this, this._triggerChange); + this.on("change", this, this._change); + } +}); + +})(); + + + +(function() { +function normalizeHash(hash, hashTypes) { + for (var prop in hash) { + if (hashTypes[prop] === 'ID') { + hash[prop + 'Binding'] = hash[prop]; + delete hash[prop]; + } + } +} + +/** + * `{{input}}` inserts a new instance of either Ember.TextField or + * Ember.Checkbox, depending on the `type` option passed in. If no `type` + * is supplied it defaults to Ember.TextField. + * + * @method input + * @for Ember.Handlebars.helpers + * @param {Hash} options + */ +Ember.Handlebars.registerHelper('input', function(options) { + Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); + + var hash = options.hash, + types = options.hashTypes, + inputType = hash.type, + onEvent = hash.on; + + delete hash.type; + delete hash.on; + + normalizeHash(hash, types); + + if (inputType === 'checkbox') { + return Ember.Handlebars.helpers.view.call(this, Ember.Checkbox, options); + } else { + hash.type = inputType; + hash.onEvent = onEvent || 'enter'; + return Ember.Handlebars.helpers.view.call(this, Ember.TextField, options); + } +}); + +/** + * `{{textarea}}` inserts a new instance of Ember.TextArea into the template + * passing its options to `Ember.TextArea`'s `create` method. + * + * @method textarea + * @for Ember.Handlebars.helpers + * @param {Hash} options + */ +Ember.Handlebars.registerHelper('textarea', function(options) { + Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); + + var hash = options.hash, + types = options.hashTypes; + + normalizeHash(hash, types); + return Ember.Handlebars.helpers.view.call(this, Ember.TextArea, options); +}); + +})(); + + + +(function() { +/*globals Handlebars */ +/** +@module ember +@submodule ember-handlebars +*/ + +/** + @private + + Find templates stored in the head tag as script tags and make them available + to `Ember.CoreView` in the global `Ember.TEMPLATES` object. This will be run + as as jQuery DOM-ready callback. + + Script tags with `text/x-handlebars` will be compiled + with Ember's Handlebars and are suitable for use as a view's template. + Those with type `text/x-raw-handlebars` will be compiled with regular + Handlebars and are suitable for use in views' computed properties. + + @method bootstrap + @for Ember.Handlebars + @static + @param ctx +*/ +Ember.Handlebars.bootstrap = function(ctx) { + var selectors = 'script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]'; + + Ember.$(selectors, ctx) + .each(function() { + // Get a reference to the script tag + var script = Ember.$(this); + + var compile = (script.attr('type') === 'text/x-raw-handlebars') ? + Ember.$.proxy(Handlebars.compile, Handlebars) : + Ember.$.proxy(Ember.Handlebars.compile, Ember.Handlebars), + // Get the name of the script, used by Ember.View's templateName property. + // First look for data-template-name attribute, then fall back to its + // id if no name is found. + templateName = script.attr('data-template-name') || script.attr('id') || 'application', + template = compile(script.html()); + + // For templates which have a name, we save them and then remove them from the DOM + Ember.TEMPLATES[templateName] = template; + + // Remove script tag from DOM + script.remove(); + }); +}; + +function bootstrap() { + Ember.Handlebars.bootstrap( Ember.$(document) ); +} + +function registerComponents(container) { + var templates = Ember.TEMPLATES, match; + if (!templates) { return; } + + for (var prop in templates) { + if (match = prop.match(/^components\/(.*)$/)) { + registerComponent(container, match[1]); + } + } +} + +function registerComponent(container, name) { + Ember.assert("You provided a template named 'components/" + name + "', but custom components must include a '-'", name.match(/-/)); + + var className = name.replace(/-/g, '_'); + var Component = container.lookupFactory('component:' + className) || container.lookupFactory('component:' + name); + var View = Component || Ember.Component.extend(); + + View.reopen({ + layoutName: 'components/' + name + }); + + Ember.Handlebars.helper(name, View); +} + +/* + We tie this to application.load to ensure that we've at least + attempted to bootstrap at the point that the application is loaded. + + We also tie this to document ready since we're guaranteed that all + the inline templates are present at this point. + + There's no harm to running this twice, since we remove the templates + from the DOM after processing. +*/ + +Ember.onLoad('Ember.Application', function(Application) { + if (Application.initializer) { + Application.initializer({ + name: 'domTemplates', + initialize: bootstrap + }); + + Application.initializer({ + name: 'registerComponents', + after: 'domTemplates', + initialize: registerComponents + }); + } else { + // for ember-old-router + Ember.onLoad('application', bootstrap); + } +}); + +})(); + + + +(function() { +/** +Ember Handlebars + +@module ember +@submodule ember-handlebars +@requires ember-views +*/ + +Ember.runLoadHooks('Ember.Handlebars', Ember.Handlebars); + +})(); + +(function() { +define("route-recognizer", + [], + function() { + "use strict"; + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + + var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); + + // A Segment represents a segment in the original route description. + // Each Segment type provides an `eachChar` and `regex` method. + // + // The `eachChar` method invokes the callback with one or more character + // specifications. A character specification consumes one or more input + // characters. + // + // The `regex` method returns a regex fragment for the segment. If the + // segment is a dynamic of star segment, the regex fragment also includes + // a capture. + // + // A character specification contains: + // + // * `validChars`: a String with a list of all valid characters, or + // * `invalidChars`: a String with a list of all invalid characters + // * `repeat`: true if the character specification can repeat + + function StaticSegment(string) { this.string = string; } + StaticSegment.prototype = { + eachChar: function(callback) { + var string = this.string, char; + + for (var i=0, l=string.length; i<l; i++) { + char = string.charAt(i); + callback({ validChars: char }); + } + }, + + regex: function() { + return this.string.replace(escapeRegex, '\\$1'); + }, + + generate: function() { + return this.string; + } + }; + + function DynamicSegment(name) { this.name = name; } + DynamicSegment.prototype = { + eachChar: function(callback) { + callback({ invalidChars: "/", repeat: true }); + }, + + regex: function() { + return "([^/]+)"; + }, + + generate: function(params) { + return params[this.name]; + } + }; + + function StarSegment(name) { this.name = name; } + StarSegment.prototype = { + eachChar: function(callback) { + callback({ invalidChars: "", repeat: true }); + }, + + regex: function() { + return "(.+)"; + }, + + generate: function(params) { + return params[this.name]; + } + }; + + function EpsilonSegment() {} + EpsilonSegment.prototype = { + eachChar: function() {}, + regex: function() { return ""; }, + generate: function() { return ""; } + }; + + function parse(route, names, types) { + // normalize route as not starting with a "/". Recognition will + // also normalize. + if (route.charAt(0) === "/") { route = route.substr(1); } + + var segments = route.split("/"), results = []; + + for (var i=0, l=segments.length; i<l; i++) { + var segment = segments[i], match; + + if (match = segment.match(/^:([^\/]+)$/)) { + results.push(new DynamicSegment(match[1])); + names.push(match[1]); + types.dynamics++; + } else if (match = segment.match(/^\*([^\/]+)$/)) { + results.push(new StarSegment(match[1])); + names.push(match[1]); + types.stars++; + } else if(segment === "") { + results.push(new EpsilonSegment()); + } else { + results.push(new StaticSegment(segment)); + types.statics++; + } + } + + return results; + } + + // A State has a character specification and (`charSpec`) and a list of possible + // subsequent states (`nextStates`). + // + // If a State is an accepting state, it will also have several additional + // properties: + // + // * `regex`: A regular expression that is used to extract parameters from paths + // that reached this accepting state. + // * `handlers`: Information on how to convert the list of captures into calls + // to registered handlers with the specified parameters + // * `types`: How many static, dynamic or star segments in this route. Used to + // decide which route to use if multiple registered routes match a path. + // + // Currently, State is implemented naively by looping over `nextStates` and + // comparing a character specification against a character. A more efficient + // implementation would use a hash of keys pointing at one or more next states. + + function State(charSpec) { + this.charSpec = charSpec; + this.nextStates = []; + } + + State.prototype = { + get: function(charSpec) { + var nextStates = this.nextStates; + + for (var i=0, l=nextStates.length; i<l; i++) { + var child = nextStates[i]; + + var isEqual = child.charSpec.validChars === charSpec.validChars; + isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars; + + if (isEqual) { return child; } + } + }, + + put: function(charSpec) { + var state; + + // If the character specification already exists in a child of the current + // state, just return that state. + if (state = this.get(charSpec)) { return state; } + + // Make a new state for the character spec + state = new State(charSpec); + + // Insert the new state as a child of the current state + this.nextStates.push(state); + + // If this character specification repeats, insert the new state as a child + // of itself. Note that this will not trigger an infinite loop because each + // transition during recognition consumes a character. + if (charSpec.repeat) { + state.nextStates.push(state); + } + + // Return the new state + return state; + }, + + // Find a list of child states matching the next character + match: function(char) { + // DEBUG "Processing `" + char + "`:" + var nextStates = this.nextStates, + child, charSpec, chars; + + // DEBUG " " + debugState(this) + var returned = []; + + for (var i=0, l=nextStates.length; i<l; i++) { + child = nextStates[i]; + + charSpec = child.charSpec; + + if (typeof (chars = charSpec.validChars) !== 'undefined') { + if (chars.indexOf(char) !== -1) { returned.push(child); } + } else if (typeof (chars = charSpec.invalidChars) !== 'undefined') { + if (chars.indexOf(char) === -1) { returned.push(child); } + } + } + + return returned; + } + + /** IF DEBUG + , debug: function() { + var charSpec = this.charSpec, + debug = "[", + chars = charSpec.validChars || charSpec.invalidChars; + + if (charSpec.invalidChars) { debug += "^"; } + debug += chars; + debug += "]"; + + if (charSpec.repeat) { debug += "+"; } + + return debug; + } + END IF **/ + }; + + /** IF DEBUG + function debug(log) { + console.log(log); + } + + function debugState(state) { + return state.nextStates.map(function(n) { + if (n.nextStates.length === 0) { return "( " + n.debug() + " [accepting] )"; } + return "( " + n.debug() + " <then> " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; + }).join(", ") + } + END IF **/ + + // This is a somewhat naive strategy, but should work in a lot of cases + // A better strategy would properly resolve /posts/:id/new and /posts/edit/:id + function sortSolutions(states) { + return states.sort(function(a, b) { + if (a.types.stars !== b.types.stars) { return a.types.stars - b.types.stars; } + if (a.types.dynamics !== b.types.dynamics) { return a.types.dynamics - b.types.dynamics; } + if (a.types.statics !== b.types.statics) { return a.types.statics - b.types.statics; } + + return 0; + }); + } + + function recognizeChar(states, char) { + var nextStates = []; + + for (var i=0, l=states.length; i<l; i++) { + var state = states[i]; + + nextStates = nextStates.concat(state.match(char)); + } + + return nextStates; + } + + function findHandler(state, path) { + var handlers = state.handlers, regex = state.regex; + var captures = path.match(regex), currentCapture = 1; + var result = []; + + for (var i=0, l=handlers.length; i<l; i++) { + var handler = handlers[i], names = handler.names, params = {}; + + for (var j=0, m=names.length; j<m; j++) { + params[names[j]] = captures[currentCapture++]; + } + + result.push({ handler: handler.handler, params: params, isDynamic: !!names.length }); + } + + return result; + } + + function addSegment(currentState, segment) { + segment.eachChar(function(char) { + var state; + + currentState = currentState.put(char); + }); + + return currentState; + } + + // The main interface + + var RouteRecognizer = function() { + this.rootState = new State(); + this.names = {}; + }; + + + RouteRecognizer.prototype = { + add: function(routes, options) { + var currentState = this.rootState, regex = "^", + types = { statics: 0, dynamics: 0, stars: 0 }, + handlers = [], allSegments = [], name; + + var isEmpty = true; + + for (var i=0, l=routes.length; i<l; i++) { + var route = routes[i], names = []; + + var segments = parse(route.path, names, types); + + allSegments = allSegments.concat(segments); + + for (var j=0, m=segments.length; j<m; j++) { + var segment = segments[j]; + + if (segment instanceof EpsilonSegment) { continue; } + + isEmpty = false; + + // Add a "/" for the new segment + currentState = currentState.put({ validChars: "/" }); + regex += "/"; + + // Add a representation of the segment to the NFA and regex + currentState = addSegment(currentState, segment); + regex += segment.regex(); + } + + handlers.push({ handler: route.handler, names: names }); + } + + if (isEmpty) { + currentState = currentState.put({ validChars: "/" }); + regex += "/"; + } + + currentState.handlers = handlers; + currentState.regex = new RegExp(regex + "$"); + currentState.types = types; + + if (name = options && options.as) { + this.names[name] = { + segments: allSegments, + handlers: handlers + }; + } + }, + + handlersFor: function(name) { + var route = this.names[name], result = []; + if (!route) { throw new Error("There is no route named " + name); } + + for (var i=0, l=route.handlers.length; i<l; i++) { + result.push(route.handlers[i]); + } + + return result; + }, + + hasRoute: function(name) { + return !!this.names[name]; + }, + + generate: function(name, params) { + var route = this.names[name], output = ""; + if (!route) { throw new Error("There is no route named " + name); } + + var segments = route.segments; + + for (var i=0, l=segments.length; i<l; i++) { + var segment = segments[i]; + + if (segment instanceof EpsilonSegment) { continue; } + + output += "/"; + output += segment.generate(params); + } + + if (output.charAt(0) !== '/') { output = '/' + output; } + + return output; + }, + + recognize: function(path) { + var states = [ this.rootState ], + pathLen, i, l; + + // DEBUG GROUP path + + if (path.charAt(0) !== "/") { path = "/" + path; } + + pathLen = path.length; + if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { + path = path.substr(0, pathLen - 1); + } + + for (i=0, l=path.length; i<l; i++) { + states = recognizeChar(states, path.charAt(i)); + if (!states.length) { break; } + } + + // END DEBUG GROUP + + var solutions = []; + for (i=0, l=states.length; i<l; i++) { + if (states[i].handlers) { solutions.push(states[i]); } + } + + states = sortSolutions(solutions); + + var state = solutions[0]; + + if (state && state.handlers) { + return findHandler(state, path); + } + } + }; + + function Target(path, matcher, delegate) { + this.path = path; + this.matcher = matcher; + this.delegate = delegate; + } + + Target.prototype = { + to: function(target, callback) { + var delegate = this.delegate; + + if (delegate && delegate.willAddRoute) { + target = delegate.willAddRoute(this.matcher.target, target); + } + + this.matcher.add(this.path, target); + + if (callback) { + if (callback.length === 0) { throw new Error("You must have an argument in the function passed to `to`"); } + this.matcher.addChild(this.path, target, callback, this.delegate); + } + } + }; + + function Matcher(target) { + this.routes = {}; + this.children = {}; + this.target = target; + } + + Matcher.prototype = { + add: function(path, handler) { + this.routes[path] = handler; + }, + + addChild: function(path, target, callback, delegate) { + var matcher = new Matcher(target); + this.children[path] = matcher; + + var match = generateMatch(path, matcher, delegate); + + if (delegate && delegate.contextEntered) { + delegate.contextEntered(target, match); + } + + callback(match); + } + }; + + function generateMatch(startingPath, matcher, delegate) { + return function(path, nestedCallback) { + var fullPath = startingPath + path; + + if (nestedCallback) { + nestedCallback(generateMatch(fullPath, matcher, delegate)); + } else { + return new Target(startingPath + path, matcher, delegate); + } + }; + } + + function addRoute(routeArray, path, handler) { + var len = 0; + for (var i=0, l=routeArray.length; i<l; i++) { + len += routeArray[i].path.length; + } + + path = path.substr(len); + routeArray.push({ path: path, handler: handler }); + } + + function eachRoute(baseRoute, matcher, callback, binding) { + var routes = matcher.routes; + + for (var path in routes) { + if (routes.hasOwnProperty(path)) { + var routeArray = baseRoute.slice(); + addRoute(routeArray, path, routes[path]); + + if (matcher.children[path]) { + eachRoute(routeArray, matcher.children[path], callback, binding); + } else { + callback.call(binding, routeArray); + } + } + } + } + + RouteRecognizer.prototype.map = function(callback, addRouteCallback) { + var matcher = new Matcher(); + + callback(generateMatch("", matcher, this.delegate)); + + eachRoute([], matcher, function(route) { + if (addRouteCallback) { addRouteCallback(this, route); } + else { this.add(route); } + }, this); + }; + return RouteRecognizer; + }); + +})(); + + + +(function() { +define("router", + ["route-recognizer", "rsvp"], + function(RouteRecognizer, RSVP) { + "use strict"; + /** + @private + + This file references several internal structures: + + ## `RecognizedHandler` + + * `{String} handler`: A handler name + * `{Object} params`: A hash of recognized parameters + + ## `HandlerInfo` + + * `{Boolean} isDynamic`: whether a handler has any dynamic segments + * `{String} name`: the name of a handler + * `{Object} handler`: a handler object + * `{Object} context`: the active context for the handler + */ + + + var slice = Array.prototype.slice; + + + + /** + @private + + A Transition is a thennable (a promise-like object) that represents + an attempt to transition to another route. It can be aborted, either + explicitly via `abort` or by attempting another transition while a + previous one is still underway. An aborted transition can also + be `retry()`d later. + */ + + function Transition(router, promise) { + this.router = router; + this.promise = promise; + this.data = {}; + this.resolvedModels = {}; + this.providedModels = {}; + this.providedModelsArray = []; + this.sequence = ++Transition.currentSequence; + this.params = {}; + } + + Transition.currentSequence = 0; + + Transition.prototype = { + targetName: null, + urlMethod: 'update', + providedModels: null, + resolvedModels: null, + params: null, + + /** + The Transition's internal promise. Calling `.then` on this property + is that same as calling `.then` on the Transition object itself, but + this property is exposed for when you want to pass around a + Transition's promise, but not the Transition object itself, since + Transition object can be externally `abort`ed, while the promise + cannot. + */ + promise: null, + + /** + Custom state can be stored on a Transition's `data` object. + This can be useful for decorating a Transition within an earlier + hook and shared with a later hook. Properties set on `data` will + be copied to new transitions generated by calling `retry` on this + transition. + */ + data: null, + + /** + A standard promise hook that resolves if the transition + succeeds and rejects if it fails/redirects/aborts. + + Forwards to the internal `promise` property which you can + use in situations where you want to pass around a thennable, + but not the Transition itself. + + @param {Function} success + @param {Function} failure + */ + then: function(success, failure) { + return this.promise.then(success, failure); + }, + + /** + Aborts the Transition. Note you can also implicitly abort a transition + by initiating another transition while a previous one is underway. + */ + abort: function() { + if (this.isAborted) { return this; } + log(this.router, this.sequence, this.targetName + ": transition was aborted"); + this.isAborted = true; + this.router.activeTransition = null; + return this; + }, + + /** + Retries a previously-aborted transition (making sure to abort the + transition if it's still active). Returns a new transition that + represents the new attempt to transition. + */ + retry: function() { + this.abort(); + + var recogHandlers = this.router.recognizer.handlersFor(this.targetName), + newTransition = performTransition(this.router, recogHandlers, this.providedModelsArray, this.params, this.data); + + return newTransition; + }, + + /** + Sets the URL-changing method to be employed at the end of a + successful transition. By default, a new Transition will just + use `updateURL`, but passing 'replace' to this method will + cause the URL to update using 'replaceWith' instead. Omitting + a parameter will disable the URL change, allowing for transitions + that don't update the URL at completion (this is also used for + handleURL, since the URL has already changed before the + transition took place). + + @param {String} method the type of URL-changing method to use + at the end of a transition. Accepted values are 'replace', + falsy values, or any other non-falsy value (which is + interpreted as an updateURL transition). + + @return {Transition} this transition + */ + method: function(method) { + this.urlMethod = method; + return this; + } + }; + + function Router() { + this.recognizer = new RouteRecognizer(); + } + + + + /** + Promise reject reasons passed to promise rejection + handlers for failed transitions. + */ + Router.UnrecognizedURLError = function(message) { + this.message = (message || "UnrecognizedURLError"); + this.name = "UnrecognizedURLError"; + }; + + Router.TransitionAborted = function(message) { + this.message = (message || "TransitionAborted"); + this.name = "TransitionAborted"; + }; + + function errorTransition(router, reason) { + return new Transition(router, RSVP.reject(reason)); + } + + + Router.prototype = { + /** + The main entry point into the router. The API is essentially + the same as the `map` method in `route-recognizer`. + + This method extracts the String handler at the last `.to()` + call and uses it as the name of the whole route. + + @param {Function} callback + */ + map: function(callback) { + this.recognizer.delegate = this.delegate; + + this.recognizer.map(callback, function(recognizer, route) { + var lastHandler = route[route.length - 1].handler; + var args = [route, { as: lastHandler }]; + recognizer.add.apply(recognizer, args); + }); + }, + + hasRoute: function(route) { + return this.recognizer.hasRoute(route); + }, + + /** + Clears the current and target route handlers and triggers exit + on each of them starting at the leaf and traversing up through + its ancestors. + */ + reset: function() { + eachHandler(this.currentHandlerInfos || [], function(handlerInfo) { + var handler = handlerInfo.handler; + if (handler.exit) { + handler.exit(); + } + }); + this.currentHandlerInfos = null; + this.targetHandlerInfos = null; + }, + + activeTransition: null, + + /** + var handler = handlerInfo.handler; + The entry point for handling a change to the URL (usually + via the back and forward button). + + Returns an Array of handlers and the parameters associated + with those parameters. + + @param {String} url a URL to process + + @return {Array} an Array of `[handler, parameter]` tuples + */ + handleURL: function(url) { + // Perform a URL-based transition, but don't change + // the URL afterward, since it already happened. + return doTransition(this, arguments).method(null); + }, + + /** + Hook point for updating the URL. + + @param {String} url a URL to update to + */ + updateURL: function() { + throw "updateURL is not implemented"; + }, + + /** + Hook point for replacing the current URL, i.e. with replaceState + + By default this behaves the same as `updateURL` + + @param {String} url a URL to update to + */ + replaceURL: function(url) { + this.updateURL(url); + }, + + /** + Transition into the specified named route. + + If necessary, trigger the exit callback on any handlers + that are no longer represented by the target route. + + @param {String} name the name of the route + */ + transitionTo: function(name) { + return doTransition(this, arguments); + }, + + /** + Identical to `transitionTo` except that the current URL will be replaced + if possible. + + This method is intended primarily for use with `replaceState`. + + @param {String} name the name of the route + */ + replaceWith: function(name) { + return doTransition(this, arguments).method('replace'); + }, + + /** + @private + + This method takes a handler name and a list of contexts and returns + a serialized parameter hash suitable to pass to `recognizer.generate()`. + + @param {String} handlerName + @param {Array[Object]} contexts + @return {Object} a serialized parameter hash + */ + paramsForHandler: function(handlerName, callback) { + return paramsForHandler(this, handlerName, slice.call(arguments, 1)); + }, + + /** + Take a named route and context objects and generate a + URL. + + @param {String} name the name of the route to generate + a URL for + @param {...Object} objects a list of objects to serialize + + @return {String} a URL + */ + generate: function(handlerName) { + var params = paramsForHandler(this, handlerName, slice.call(arguments, 1)); + return this.recognizer.generate(handlerName, params); + }, + + isActive: function(handlerName) { + var contexts = slice.call(arguments, 1); + + var targetHandlerInfos = this.targetHandlerInfos, + found = false, names, object, handlerInfo, handlerObj; + + if (!targetHandlerInfos) { return false; } + + for (var i=targetHandlerInfos.length-1; i>=0; i--) { + handlerInfo = targetHandlerInfos[i]; + if (handlerInfo.name === handlerName) { found = true; } + + if (found) { + if (contexts.length === 0) { break; } + + if (handlerInfo.isDynamic) { + object = contexts.pop(); + if (handlerInfo.context !== object) { return false; } + } + } + } + + return contexts.length === 0 && found; + }, + + trigger: function(name) { + var args = slice.call(arguments); + trigger(this.currentHandlerInfos, false, args); + }, + + /** + Hook point for logging transition status updates. + + @param {String} message The message to log. + */ + log: null + }; + + /** + @private + + Used internally for both URL and named transition to determine + a shared pivot parent route and other data necessary to perform + a transition. + */ + function getMatchPoint(router, handlers, objects, inputParams) { + + var objectsToMatch = objects.length, + matchPoint = handlers.length, + providedModels = {}, i, + currentHandlerInfos = router.currentHandlerInfos || [], + params = {}, + oldParams = router.currentParams || {}, + activeTransition = router.activeTransition, + handlerParams = {}; + + merge(params, inputParams); + + for (i = handlers.length - 1; i >= 0; i--) { + var handlerObj = handlers[i], + handlerName = handlerObj.handler, + oldHandlerInfo = currentHandlerInfos[i], + hasChanged = false; + + // Check if handler names have changed. + if (!oldHandlerInfo || oldHandlerInfo.name !== handlerObj.handler) { hasChanged = true; } + + if (handlerObj.isDynamic) { + // URL transition. + + if (objectsToMatch > 0) { + hasChanged = true; + providedModels[handlerName] = objects[--objectsToMatch]; + } else { + handlerParams[handlerName] = {}; + for (var prop in handlerObj.params) { + if (!handlerObj.params.hasOwnProperty(prop)) { continue; } + var newParam = handlerObj.params[prop]; + if (oldParams[prop] !== newParam) { hasChanged = true; } + handlerParams[handlerName][prop] = params[prop] = newParam; + } + } + } else if (handlerObj.hasOwnProperty('names') && handlerObj.names.length) { + // Named transition. + + if (objectsToMatch > 0) { + hasChanged = true; + providedModels[handlerName] = objects[--objectsToMatch]; + } else if (activeTransition && activeTransition.providedModels[handlerName]) { + + // Use model from previous transition attempt, preferably the resolved one. + hasChanged = true; + providedModels[handlerName] = activeTransition.providedModels[handlerName] || + activeTransition.resolvedModels[handlerName]; + } else { + var names = handlerObj.names; + handlerParams[handlerName] = {}; + for (var j = 0, len = names.length; j < len; ++j) { + var name = names[j]; + handlerParams[handlerName][name] = params[name] = oldParams[name]; + } + } + } + + if (hasChanged) { matchPoint = i; } + } + + if (objectsToMatch > 0) { + throw "More context objects were passed than there are dynamic segments for the route: " + handlers[handlers.length - 1].handler; + } + + return { matchPoint: matchPoint, providedModels: providedModels, params: params, handlerParams: handlerParams }; + } + + /** + @private + + This method takes a handler name and a list of contexts and returns + a serialized parameter hash suitable to pass to `recognizer.generate()`. + + @param {Router} router + @param {String} handlerName + @param {Array[Object]} objects + @return {Object} a serialized parameter hash + */ + function paramsForHandler(router, handlerName, objects) { + + var handlers = router.recognizer.handlersFor(handlerName), + params = {}, + matchPoint = getMatchPoint(router, handlers, objects).matchPoint, + object, handlerObj, handler, names, i; + + for (i=0; i<handlers.length; i++) { + handlerObj = handlers[i]; + handler = router.getHandler(handlerObj.handler); + names = handlerObj.names; + + // If it's a dynamic segment + if (names.length) { + // If we have objects, use them + if (i >= matchPoint) { + object = objects.shift(); + // Otherwise use existing context + } else { + object = handler.context; + } + + // Serialize to generate params + merge(params, serialize(handler, object, names)); + } + } + return params; + } + + function merge(hash, other) { + for (var prop in other) { + if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; } + } + } + + /** + @private + */ + function createNamedTransition(router, args) { + var handlers = router.recognizer.handlersFor(args[0]); + + log(router, "Attempting transition to " + args[0]); + + return performTransition(router, handlers, slice.call(args, 1), router.currentParams); + } + + /** + @private + */ + function createURLTransition(router, url) { + + var results = router.recognizer.recognize(url), + currentHandlerInfos = router.currentHandlerInfos; + + log(router, "Attempting URL transition to " + url); + + if (!results) { + return errorTransition(router, new Router.UnrecognizedURLError(url)); + } + + return performTransition(router, results, [], {}); + } + + + /** + @private + + Takes an Array of `HandlerInfo`s, figures out which ones are + exiting, entering, or changing contexts, and calls the + proper handler hooks. + + For example, consider the following tree of handlers. Each handler is + followed by the URL segment it handles. + + ``` + |~index ("/") + | |~posts ("/posts") + | | |-showPost ("/:id") + | | |-newPost ("/new") + | | |-editPost ("/edit") + | |~about ("/about/:id") + ``` + + Consider the following transitions: + + 1. A URL transition to `/posts/1`. + 1. Triggers the `*model` callbacks on the + `index`, `posts`, and `showPost` handlers + 2. Triggers the `enter` callback on the same + 3. Triggers the `setup` callback on the same + 2. A direct transition to `newPost` + 1. Triggers the `exit` callback on `showPost` + 2. Triggers the `enter` callback on `newPost` + 3. Triggers the `setup` callback on `newPost` + 3. A direct transition to `about` with a specified + context object + 1. Triggers the `exit` callback on `newPost` + and `posts` + 2. Triggers the `serialize` callback on `about` + 3. Triggers the `enter` callback on `about` + 4. Triggers the `setup` callback on `about` + + @param {Transition} transition + @param {Array[HandlerInfo]} handlerInfos + */ + function setupContexts(transition, handlerInfos) { + var router = transition.router, + partition = partitionHandlers(router.currentHandlerInfos || [], handlerInfos); + + router.targetHandlerInfos = handlerInfos; + + eachHandler(partition.exited, function(handlerInfo) { + var handler = handlerInfo.handler; + delete handler.context; + if (handler.exit) { handler.exit(); } + }); + + var currentHandlerInfos = partition.unchanged.slice(); + router.currentHandlerInfos = currentHandlerInfos; + + eachHandler(partition.updatedContext, function(handlerInfo) { + handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, false); + }); + + eachHandler(partition.entered, function(handlerInfo) { + handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, true); + }); + + if (router.didTransition) { + router.didTransition(handlerInfos); + } + } + + /** + @private + + Helper method used by setupContexts. Handles errors or redirects + that may happen in enter/setup. + */ + function handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, enter) { + var handler = handlerInfo.handler, + context = handlerInfo.context; + + try { + if (enter && handler.enter) { handler.enter(); } + checkAbort(transition); + + setContext(handler, context); + + if (handler.setup) { handler.setup(context); } + checkAbort(transition); + } catch(e) { + if (!(e instanceof Router.TransitionAborted)) { + // Trigger the `error` event starting from this failed handler. + trigger(currentHandlerInfos.concat(handlerInfo), true, ['error', e, transition]); + } + + // Propagate the error so that the transition promise will reject. + throw e; + } + + currentHandlerInfos.push(handlerInfo); + } + + + /** + @private + + Iterates over an array of `HandlerInfo`s, passing the handler + and context into the callback. + + @param {Array[HandlerInfo]} handlerInfos + @param {Function(Object, Object)} callback + */ + function eachHandler(handlerInfos, callback) { + for (var i=0, l=handlerInfos.length; i<l; i++) { + callback(handlerInfos[i]); + } + } + + /** + @private + + This function is called when transitioning from one URL to + another to determine which handlers are not longer active, + which handlers are newly active, and which handlers remain + active but have their context changed. + + Take a list of old handlers and new handlers and partition + them into four buckets: + + * unchanged: the handler was active in both the old and + new URL, and its context remains the same + * updated context: the handler was active in both the + old and new URL, but its context changed. The handler's + `setup` method, if any, will be called with the new + context. + * exited: the handler was active in the old URL, but is + no longer active. + * entered: the handler was not active in the old URL, but + is now active. + + The PartitionedHandlers structure has four fields: + + * `updatedContext`: a list of `HandlerInfo` objects that + represent handlers that remain active but have a changed + context + * `entered`: a list of `HandlerInfo` objects that represent + handlers that are newly active + * `exited`: a list of `HandlerInfo` objects that are no + longer active. + * `unchanged`: a list of `HanderInfo` objects that remain active. + + @param {Array[HandlerInfo]} oldHandlers a list of the handler + information for the previous URL (or `[]` if this is the + first handled transition) + @param {Array[HandlerInfo]} newHandlers a list of the handler + information for the new URL + + @return {Partition} + */ + function partitionHandlers(oldHandlers, newHandlers) { + var handlers = { + updatedContext: [], + exited: [], + entered: [], + unchanged: [] + }; + + var handlerChanged, contextChanged, i, l; + + for (i=0, l=newHandlers.length; i<l; i++) { + var oldHandler = oldHandlers[i], newHandler = newHandlers[i]; + + if (!oldHandler || oldHandler.handler !== newHandler.handler) { + handlerChanged = true; + } + + if (handlerChanged) { + handlers.entered.push(newHandler); + if (oldHandler) { handlers.exited.unshift(oldHandler); } + } else if (contextChanged || oldHandler.context !== newHandler.context) { + contextChanged = true; + handlers.updatedContext.push(newHandler); + } else { + handlers.unchanged.push(oldHandler); + } + } + + for (i=newHandlers.length, l=oldHandlers.length; i<l; i++) { + handlers.exited.unshift(oldHandlers[i]); + } + + return handlers; + } + + function trigger(handlerInfos, ignoreFailure, args) { + + var name = args.shift(); + + if (!handlerInfos) { + if (ignoreFailure) { return; } + throw new Error("Could not trigger event '" + name + "'. There are no active handlers"); + } + + var eventWasHandled = false; + + for (var i=handlerInfos.length-1; i>=0; i--) { + var handlerInfo = handlerInfos[i], + handler = handlerInfo.handler; + + if (handler.events && handler.events[name]) { + if (handler.events[name].apply(handler, args) === true) { + eventWasHandled = true; + } else { + return; + } + } + } + + if (!eventWasHandled && !ignoreFailure) { + throw new Error("Nothing handled the event '" + name + "'."); + } + } + + function setContext(handler, context) { + handler.context = context; + if (handler.contextDidChange) { handler.contextDidChange(); } + } + + /** + @private + + Creates, begins, and returns a Transition. + */ + function performTransition(router, recogHandlers, providedModelsArray, params, data) { + + var matchPointResults = getMatchPoint(router, recogHandlers, providedModelsArray, params), + targetName = recogHandlers[recogHandlers.length - 1].handler, + wasTransitioning = false; + + // Check if there's already a transition underway. + if (router.activeTransition) { + if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray)) { + return router.activeTransition; + } + router.activeTransition.abort(); + wasTransitioning = true; + } + + var deferred = RSVP.defer(), + transition = new Transition(router, deferred.promise); + + transition.targetName = targetName; + transition.providedModels = matchPointResults.providedModels; + transition.providedModelsArray = providedModelsArray; + transition.params = matchPointResults.params; + transition.data = data || {}; + router.activeTransition = transition; + + var handlerInfos = generateHandlerInfos(router, recogHandlers); + + // Fire 'willTransition' event on current handlers, but don't fire it + // if a transition was already underway. + if (!wasTransitioning) { + trigger(router.currentHandlerInfos, true, ['willTransition', transition]); + } + + log(router, transition.sequence, "Beginning validation for transition to " + transition.targetName); + validateEntry(transition, handlerInfos, 0, matchPointResults.matchPoint, matchPointResults.handlerParams) + .then(transitionSuccess, transitionFailure); + + return transition; + + function transitionSuccess() { + checkAbort(transition); + + try { + finalizeTransition(transition, handlerInfos); + + // Resolve with the final handler. + deferred.resolve(handlerInfos[handlerInfos.length - 1].handler); + } catch(e) { + deferred.reject(e); + } + + // Don't nullify if another transition is underway (meaning + // there was a transition initiated with enter/setup). + if (!transition.isAborted) { + router.activeTransition = null; + } + } + + function transitionFailure(reason) { + deferred.reject(reason); + } + } + + /** + @private + + Accepts handlers in Recognizer format, either returned from + recognize() or handlersFor(), and returns unified + `HandlerInfo`s. + */ + function generateHandlerInfos(router, recogHandlers) { + var handlerInfos = []; + for (var i = 0, len = recogHandlers.length; i < len; ++i) { + var handlerObj = recogHandlers[i], + isDynamic = handlerObj.isDynamic || (handlerObj.names && handlerObj.names.length); + + handlerInfos.push({ + isDynamic: !!isDynamic, + name: handlerObj.handler, + handler: router.getHandler(handlerObj.handler) + }); + } + return handlerInfos; + } + + /** + @private + */ + function transitionsIdentical(oldTransition, targetName, providedModelsArray) { + + if (oldTransition.targetName !== targetName) { return false; } + + var oldModels = oldTransition.providedModelsArray; + if (oldModels.length !== providedModelsArray.length) { return false; } + + for (var i = 0, len = oldModels.length; i < len; ++i) { + if (oldModels[i] !== providedModelsArray[i]) { return false; } + } + return true; + } + + /** + @private + + Updates the URL (if necessary) and calls `setupContexts` + to update the router's array of `currentHandlerInfos`. + */ + function finalizeTransition(transition, handlerInfos) { + + var router = transition.router, + seq = transition.sequence, + handlerName = handlerInfos[handlerInfos.length - 1].name; + + log(router, seq, "Validation succeeded, finalizing transition;"); + + // Collect params for URL. + var objects = []; + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + if (handlerInfo.isDynamic) { + objects.push(handlerInfo.context); + } + } + + var params = paramsForHandler(router, handlerName, objects); + + transition.providedModelsArray = []; + transition.providedContexts = {}; + router.currentParams = params; + + var urlMethod = transition.urlMethod; + if (urlMethod) { + var url = router.recognizer.generate(handlerName, params); + + if (urlMethod === 'replace') { + router.replaceURL(url); + } else { + // Assume everything else is just a URL update for now. + router.updateURL(url); + } + } + + setupContexts(transition, handlerInfos); + log(router, seq, "TRANSITION COMPLETE."); + } + + /** + @private + + Internal function used to construct the chain of promises used + to validate a transition. Wraps calls to `beforeModel`, `model`, + and `afterModel` in promises, and checks for redirects/aborts + between each. + */ + function validateEntry(transition, handlerInfos, index, matchPoint, handlerParams) { + + if (index === handlerInfos.length) { + // No more contexts to resolve. + return RSVP.resolve(transition.resolvedModels); + } + + var router = transition.router, + handlerInfo = handlerInfos[index], + handler = handlerInfo.handler, + handlerName = handlerInfo.name, + seq = transition.sequence, + errorAlreadyHandled = false, + resolvedModel; + + if (index < matchPoint) { + log(router, seq, handlerName + ": using context from already-active handler"); + + // We're before the match point, so don't run any hooks, + // just use the already resolved context from the handler. + resolvedModel = handlerInfo.handler.context; + return proceed(); + } + + return RSVP.resolve().then(handleAbort) + .then(beforeModel) + .then(null, handleError) + .then(handleAbort) + .then(model) + .then(null, handleError) + .then(handleAbort) + .then(afterModel) + .then(null, handleError) + .then(handleAbort) + .then(proceed); + + function handleAbort(result) { + + if (transition.isAborted) { + log(transition.router, transition.sequence, "detected abort."); + errorAlreadyHandled = true; + return RSVP.reject(new Router.TransitionAborted()); + } + + return result; + } + + function handleError(reason) { + + if (errorAlreadyHandled) { return RSVP.reject(reason); } + errorAlreadyHandled = true; + transition.abort(); + + log(router, seq, handlerName + ": handling error: " + reason); + + // An error was thrown / promise rejected, so fire an + // `error` event from this handler info up to root. + trigger(handlerInfos.slice(0, index + 1), true, ['error', reason, transition]); + + if (handler.error) { + handler.error(reason, transition); } + + + // Propagate the original error. + return RSVP.reject(reason); + } + + function beforeModel() { + + log(router, seq, handlerName + ": calling beforeModel hook"); + + return handler.beforeModel && handler.beforeModel(transition); + } + + function model() { + log(router, seq, handlerName + ": resolving model"); + + return getModel(handlerInfo, transition, handlerParams[handlerName], index >= matchPoint); + } + + function afterModel(context) { + + log(router, seq, handlerName + ": calling afterModel hook"); + + // Pass the context and resolved parent contexts to afterModel, but we don't + // want to use the value returned from `afterModel` in any way, but rather + // always resolve with the original `context` object. + + resolvedModel = context; + return handler.afterModel && handler.afterModel(resolvedModel, transition); + } + + function proceed() { + log(router, seq, handlerName + ": validation succeeded, proceeding"); + + handlerInfo.context = transition.resolvedModels[handlerInfo.name] = resolvedModel; + return validateEntry(transition, handlerInfos, index + 1, matchPoint, handlerParams); + } + } + + /** + @private + + Throws a TransitionAborted if the provided transition has been aborted. + */ + function checkAbort(transition) { + if (transition.isAborted) { + log(transition.router, transition.sequence, "detected abort."); + throw new Router.TransitionAborted(); + } + } + + /** + @private + + Encapsulates the logic for whether to call `model` on a route, + or use one of the models provided to `transitionTo`. + */ + function getModel(handlerInfo, transition, handlerParams, needsUpdate) { + + var handler = handlerInfo.handler, + handlerName = handlerInfo.name; + + if (!needsUpdate && handler.hasOwnProperty('context')) { + return handler.context; + } + + if (handlerInfo.isDynamic && transition.providedModels.hasOwnProperty(handlerName)) { + var providedModel = transition.providedModels[handlerName]; + return typeof providedModel === 'function' ? providedModel() : providedModel; + } + + return handler.model && handler.model(handlerParams || {}, transition); + } + + /** + @private + */ + function log(router, sequence, msg) { + + if (!router.log) { return; } + + if (arguments.length === 3) { + router.log("Transition #" + sequence + ": " + msg); + } else { + msg = sequence; + router.log(msg); + } + } + + /** + @private + + Begins and returns a Transition based on the provided + arguments. Accepts arguments in the form of both URL + transitions and named transitions. + + @param {Router} router + @param {Array[Object]} args arguments passed to transitionTo, + replaceWith, or handleURL + */ + function doTransition(router, args) { + // Normalize blank transitions to root URL transitions. + var name = args[0] || '/'; + + if (name.charAt(0) === '/') { + return createURLTransition(router, name); + } else { + return createNamedTransition(router, args); + } + } + + /** + @private + + Serializes a handler using its custom `serialize` method or + by a default that looks up the expected property name from + the dynamic segment. + + @param {Object} handler a router handler + @param {Object} model the model to be serialized for this handler + @param {Array[Object]} names the names array attached to an + handler object returned from router.recognizer.handlersFor() + */ + function serialize(handler, model, names) { + + // Use custom serialize if it exists. + if (handler.serialize) { + return handler.serialize(model, names); + } + + if (names.length !== 1) { return; } + + var name = names[0], object = {}; + + if (/_id$/.test(name)) { + object[name] = model.id; + } else { + object[name] = model; + } + return object; + } + + return Router; + }); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +function DSL(name) { + this.parent = name; + this.matches = []; +} + +DSL.prototype = { + resource: function(name, options, callback) { + if (arguments.length === 2 && typeof options === 'function') { + callback = options; + options = {}; + } + + if (arguments.length === 1) { + options = {}; + } + + if (typeof options.path !== 'string') { + options.path = "/" + name; + } + + if (callback) { + var dsl = new DSL(name); + callback.call(dsl); + this.push(options.path, name, dsl.generate()); + } else { + this.push(options.path, name); + } + }, + + push: function(url, name, callback) { + var parts = name.split('.'); + if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } + + this.matches.push([url, name, callback]); + }, + + route: function(name, options) { + Ember.assert("You must use `this.resource` to nest", typeof options !== 'function'); + + options = options || {}; + + if (typeof options.path !== 'string') { + options.path = "/" + name; + } + + if (this.parent && this.parent !== 'application') { + name = this.parent + "." + name; + } + + this.push(options.path, name); + }, + + generate: function() { + var dslMatches = this.matches; + + if (!this.explicitIndex) { + this.route("index", { path: "/" }); + } + + return function(match) { + for (var i=0, l=dslMatches.length; i<l; i++) { + var dslMatch = dslMatches[i]; + match(dslMatch[0]).to(dslMatch[1], dslMatch[2]); + } + }; + } +}; + +DSL.map = function(callback) { + var dsl = new DSL(); + callback.call(dsl); + return dsl; +}; + +Ember.RouterDSL = DSL; + +})(); + + + +(function() { +var get = Ember.get; + +/** +@module ember +@submodule ember-routing +*/ + +Ember.controllerFor = function(container, controllerName, context, lookupOptions) { + return container.lookup('controller:' + controllerName, lookupOptions) || + Ember.generateController(container, controllerName, context); +}; +/* + Generates a controller automatically if none was provided. + The type of generated controller depends on the context. + You can customize your generated controllers by defining + `App.ObjectController` and `App.ArrayController` +*/ +Ember.generateController = function(container, controllerName, context) { + var controller, DefaultController, fullName, instance; + + if (context && Ember.isArray(context)) { + DefaultController = container.resolve('controller:array'); + controller = DefaultController.extend({ + content: context + }); + } else if (context) { + DefaultController = container.resolve('controller:object'); + controller = DefaultController.extend({ + content: context + }); + } else { + DefaultController = container.resolve('controller:basic'); + controller = DefaultController.extend(); + } + + controller.toString = function() { + return "(generated " + controllerName + " controller)"; + }; + + fullName = 'controller:' + controllerName; + container.register(fullName, controller); + + instance = container.lookup(fullName); + + if (get(instance, 'namespace.LOG_ACTIVE_GENERATION')) { + Ember.Logger.info("generated -> " + fullName, { fullName: fullName }); + } + + return instance; +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var Router = requireModule("router"); +var get = Ember.get, set = Ember.set; +var defineProperty = Ember.defineProperty; + +var DefaultView = Ember._MetamorphView; +function setupLocation(router) { + var location = get(router, 'location'), + rootURL = get(router, 'rootURL'), + options = {}; + + if (typeof rootURL === 'string') { + options.rootURL = rootURL; + } + + if ('string' === typeof location) { + options.implementation = location; + location = set(router, 'location', Ember.Location.create(options)); + + } +} + +/** + The `Ember.Router` class manages the application state and URLs. Refer to + the [routing guide](http://emberjs.com/guides/routing/) for documentation. + + @class Router + @namespace Ember + @extends Ember.Object +*/ +Ember.Router = Ember.Object.extend({ + location: 'hash', + + init: function() { + this.router = this.constructor.router || this.constructor.map(Ember.K); + this._activeViews = {}; + setupLocation(this); + }, + + url: Ember.computed(function() { + return get(this, 'location').getURL(); + }), + + startRouting: function() { + this.router = this.router || this.constructor.map(Ember.K); + + var router = this.router, + location = get(this, 'location'), + container = this.container, + self = this; + + setupRouter(this, router, location); + + container.register('view:default', DefaultView); + container.register('view:toplevel', Ember.View.extend()); + + location.onUpdateURL(function(url) { + self.handleURL(url); + }); + + this.handleURL(location.getURL()); + }, + + didTransition: function(infos) { + var appController = this.container.lookup('controller:application'), + path = routePath(infos); + + if (!('currentPath' in appController)) { + defineProperty(appController, 'currentPath'); + } + set(appController, 'currentPath', path); + this.notifyPropertyChange('url'); + + if (get(this, 'namespace').LOG_TRANSITIONS) { + Ember.Logger.log("Transitioned into '" + path + "'"); + } + }, + + handleURL: function(url) { + scheduleLoadingStateEntry(this); + + var self = this; + + return this.router.handleURL(url).then(function() { + transitionCompleted(self); + }); + }, + + transitionTo: function() { + return doTransition(this, 'transitionTo', arguments); + }, + + replaceWith: function() { + return doTransition(this, 'replaceWith', arguments); + }, + + generate: function() { + var url = this.router.generate.apply(this.router, arguments); + return this.location.formatURL(url); + }, + + isActive: function(routeName) { + var router = this.router; + return router.isActive.apply(router, arguments); + }, + + send: function(name, context) { + this.router.trigger.apply(this.router, arguments); + }, + + hasRoute: function(route) { + return this.router.hasRoute(route); + }, + + /** + @private + + Resets the state of the router by clearing the current route + handlers and deactivating them. + + @method reset + */ + reset: function() { + this.router.reset(); + }, + + _lookupActiveView: function(templateName) { + var active = this._activeViews[templateName]; + return active && active[0]; + }, + + _connectActiveView: function(templateName, view) { + var existing = this._activeViews[templateName]; + + if (existing) { + existing[0].off('willDestroyElement', this, existing[1]); + } + + var disconnect = function() { + delete this._activeViews[templateName]; + }; + + this._activeViews[templateName] = [view, disconnect]; + view.one('willDestroyElement', this, disconnect); + } +}); + +function getHandlerFunction(router) { + var seen = {}, container = router.container, + DefaultRoute = container.resolve('route:basic'); + + return function(name) { + var routeName = 'route:' + name, + handler = container.lookup(routeName); + + if (seen[name]) { return handler; } + + seen[name] = true; + + if (!handler) { + if (name === 'loading') { return {}; } + + container.register(routeName, DefaultRoute.extend()); + handler = container.lookup(routeName); + + if (get(router, 'namespace.LOG_ACTIVE_GENERATION')) { + Ember.Logger.info("generated -> " + routeName, { fullName: routeName }); + } + } + + if (name === 'application') { + // Inject default `error` handler. + handler.events = handler.events || {}; + handler.events.error = handler.events.error || defaultErrorHandler; + } + + handler.routeName = name; + return handler; + }; +} + +function defaultErrorHandler(error, transition) { + Ember.Logger.error('Error while loading route:', error); + + // Using setTimeout allows us to escape from the Promise's try/catch block + setTimeout(function() { throw error; }); +} + + +function routePath(handlerInfos) { + var path = []; + + for (var i=1, l=handlerInfos.length; i<l; i++) { + var name = handlerInfos[i].name, + nameParts = name.split("."); + + path.push(nameParts[nameParts.length - 1]); + } + + return path.join("."); +} + +function setupRouter(emberRouter, router, location) { + var lastURL; + + router.getHandler = getHandlerFunction(emberRouter); + + var doUpdateURL = function() { + location.setURL(lastURL); + }; + + router.updateURL = function(path) { + lastURL = path; + Ember.run.once(doUpdateURL); + }; + + if (location.replaceURL) { + var doReplaceURL = function() { + location.replaceURL(lastURL); + }; + + router.replaceURL = function(path) { + lastURL = path; + Ember.run.once(doReplaceURL); + }; + } + + router.didTransition = function(infos) { + emberRouter.didTransition(infos); + }; +} + +function doTransition(router, method, args) { + // Normalize blank route to root URL. + args = [].slice.call(args); + args[0] = args[0] || '/'; + + var passedName = args[0], name; + + if (passedName.charAt(0) === '/') { + name = passedName; + } else { + if (!router.router.hasRoute(passedName)) { + name = args[0] = passedName + '.index'; + } else { + name = passedName; + } + + Ember.assert("The route " + passedName + " was not found", router.router.hasRoute(name)); + } + + scheduleLoadingStateEntry(router); + + var transitionPromise = router.router[method].apply(router.router, args); + transitionPromise.then(function() { + transitionCompleted(router); + }); + + // We want to return the configurable promise object + // so that callers of this function can use `.method()` on it, + // which obviously doesn't exist for normal RSVP promises. + return transitionPromise; +} + +function scheduleLoadingStateEntry(router) { + if (router._loadingStateActive) { return; } + router._shouldEnterLoadingState = true; + Ember.run.scheduleOnce('routerTransitions', null, enterLoadingState, router); +} + +function enterLoadingState(router) { + if (router._loadingStateActive || !router._shouldEnterLoadingState) { return; } + + var loadingRoute = router.router.getHandler('loading'); + if (loadingRoute) { + if (loadingRoute.enter) { loadingRoute.enter(); } + if (loadingRoute.setup) { loadingRoute.setup(); } + router._loadingStateActive = true; + } +} + +function exitLoadingState(router) { + router._shouldEnterLoadingState = false; + if (!router._loadingStateActive) { return; } + + var loadingRoute = router.router.getHandler('loading'); + if (loadingRoute && loadingRoute.exit) { loadingRoute.exit(); } + router._loadingStateActive = false; +} + +function transitionCompleted(router) { + router.notifyPropertyChange('url'); + exitLoadingState(router); +} + +Ember.Router.reopenClass({ + map: function(callback) { + var router = this.router = new Router(); + + if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { + router.log = Ember.Logger.debug; + } + + var dsl = Ember.RouterDSL.map(function() { + this.resource('application', { path: "/" }, function() { + callback.call(this); + }); + }); + + router.map(dsl.generate()); + return router; + } +}); + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set, + classify = Ember.String.classify, + fmt = Ember.String.fmt, + a_forEach = Ember.EnumerableUtils.forEach, + a_replace = Ember.EnumerableUtils.replace; + +/** + The `Ember.Route` class is used to define individual routes. Refer to + the [routing guide](http://emberjs.com/guides/routing/) for documentation. + + @class Route + @namespace Ember + @extends Ember.Object +*/ +Ember.Route = Ember.Object.extend({ + /** + @private + + @method exit + */ + exit: function() { + this.deactivate(); + this.teardownViews(); + }, + + /** + @private + + @method enter + */ + enter: function() { + this.activate(); + }, + + /** + The collection of functions, keyed by name, available on this route as + action targets. + + These functions will be invoked when a matching `{{action}}` is triggered + from within a template and the application's current route is this route. + + Events can also be invoked from other parts of your application via `Route#send` + or `Controller#send`. + + The context of the event will be this route. + + ## Bubbling + + By default, an event will stop bubbling once a handler defined + on the `events` hash handles it. To continue bubbling the event, + you must return `true` from the handler. + + ## Built-in events + + There are a few built-in events pertaining to transitions that you + can use to customize transition behavior: `willTransition` and + `error`. + + ### `willTransition` + + The `willTransition` event is fired at the beginning of any + attempted transition with a `Transition` object as the sole + argument. This event can be used for aborting, redirecting, + or decorating the transition from the currently active routes. + + A good example is preventing navigation when a form is + half-filled out: + + ```js + App.ContactFormRoute = Ember.Route.extend({ + events: { + willTransition: function(transition) { + if (this.controller.get('userHasEnteredData')) { + this.controller.displayNavigationConfirm(); + transition.abort(); + } + } + } + }); + ``` + + You can also redirect elsewhere by calling + `this.transitionTo('elsewhere')` from within `willTransition`. + Note that `willTransition` will not be fired for the + redirecting `transitionTo`, since `willTransition` doesn't + fire when there is already a transition underway. If you want + subsequent `willTransition` events to fire for the redirecting + transition, you must first explicitly call + `transition.abort()`. + + ### `error` + + When attempting to transition into a route, any of the hooks + may throw an error, or return a promise that rejects, at which + point an `error` event will be fired on the partially-entered + routes, allowing for per-route error handling logic, or shared + error handling logic defined on a parent route. + + Here is an example of an error handler that will be invoked + for rejected promises / thrown errors from the various hooks + on the route, as well as any unhandled errors from child + routes: + + ```js + App.AdminRoute = Ember.Route.extend({ + beforeModel: function() { + throw "bad things!"; + // ...or, equivalently: + return Ember.RSVP.reject("bad things!"); + }, + + events: { + error: function(error, transition) { + // Assuming we got here due to the error in `beforeModel`, + // we can expect that error === "bad things!", + // but a promise model rejecting would also + // call this hook, as would any errors encountered + // in `afterModel`. + + // The `error` hook is also provided the failed + // `transition`, which can be stored and later + // `.retry()`d if desired. + + this.transitionTo('login'); + } + } + }); + ``` + + `error` events that bubble up all the way to `ApplicationRoute` + will fire a default error handler that logs the error. You can + specify your own global default error handler by overriding the + `error` handler on `ApplicationRoute`: + + ```js + App.ApplicationRoute = Ember.Route.extend({ + events: { + error: function(error, transition) { + this.controllerFor('banner').displayError(error.message); + } + } + }); + ``` + + @see {Ember.Route#send} + @see {Handlebars.helpers.action} + + @property events + @type Hash + @default null + */ + events: null, + + /** + This hook is executed when the router completely exits this route. It is + not executed when the model for the route changes. + + @method deactivate + */ + deactivate: Ember.K, + + /** + This hook is executed when the router enters the route for the first time. + It is not executed when the model for the route changes. + + @method activate + */ + activate: Ember.K, + + /** + Transition into another route. Optionally supply a model for the + route in question. The model will be serialized into the URL + using the `serialize` hook. + + @method transitionTo + @param {String} name the name of the route + @param {...Object} models the + */ + transitionTo: function(name, context) { + var router = this.router; + return router.transitionTo.apply(router, arguments); + }, + + /** + Transition into another route while replacing the current URL if + possible. Identical to `transitionTo` in all other respects. + + Of the bundled location types, only `history` currently supports + this behavior. + + @method replaceWith + @param {String} name the name of the route + @param {...Object} models the + */ + replaceWith: function() { + var router = this.router; + return this.router.replaceWith.apply(this.router, arguments); + }, + + send: function() { + return this.router.send.apply(this.router, arguments); + }, + + /** + @private + + This hook is the entry point for router.js + + @method setup + */ + setup: function(context) { + var controller = this.controllerFor(this.routeName, context); + + // Assign the route's controller so that it can more easily be + // referenced in event handlers + this.controller = controller; + + if (this.setupControllers) { + Ember.deprecate("Ember.Route.setupControllers is deprecated. Please use Ember.Route.setupController(controller, model) instead."); + this.setupControllers(controller, context); + } else { + this.setupController(controller, context); + } + + if (this.renderTemplates) { + Ember.deprecate("Ember.Route.renderTemplates is deprecated. Please use Ember.Route.renderTemplate(controller, model) instead."); + this.renderTemplates(context); + } else { + this.renderTemplate(controller, context); + } + }, + + /** + @deprecated + + A hook you can implement to optionally redirect to another route. + + If you call `this.transitionTo` from inside of this hook, this route + will not be entered in favor of the other hook. + + This hook is deprecated in favor of using the `afterModel` hook + for performing redirects after the model has resolved. + + @method redirect + @param {Object} model the model for this route + */ + redirect: Ember.K, + + /** + This hook is the first of the route entry validation hooks + called when an attempt is made to transition into a route + or one of its children. It is called before `model` and + `afterModel`, and is appropriate for cases when: + + 1) A decision can be made to redirect elsewhere without + needing to resolve the model first. + 2) Any async operations need to occur first before the + model is attempted to be resolved. + + This hook is provided the current `transition` attempt + as a parameter, which can be used to `.abort()` the transition, + save it for a later `.retry()`, or retrieve values set + on it from a previous hook. You can also just call + `this.transitionTo` to another route to implicitly + abort the `transition`. + + You can return a promise from this hook to pause the + transition until the promise resolves (or rejects). This could + be useful, for instance, for retrieving async code from + the server that is required to enter a route. + + ```js + App.PostRoute = Ember.Route.extend({ + beforeModel: function(transition) { + if (!App.Post) { + return Ember.$.getScript('/models/post.js'); + } + } + }); + ``` + + If `App.Post` doesn't exist in the above example, + `beforeModel` will use jQuery's `getScript`, which + returns a promise that resolves after the server has + successfully retrieved and executed the code from the + server. Note that if an error were to occur, it would + be passed to the `error` hook on `Ember.Route`, but + it's also possible to handle errors specific to + `beforeModel` right from within the hook (to distinguish + from the shared error handling behavior of the `error` + hook): + + ```js + App.PostRoute = Ember.Route.extend({ + beforeModel: function(transition) { + if (!App.Post) { + var self = this; + return Ember.$.getScript('post.js').then(null, function(e) { + self.transitionTo('help'); + + // Note that the above transitionTo will implicitly + // halt the transition. If you were to return + // nothing from this promise reject handler, + // according to promise semantics, that would + // convert the reject into a resolve and the + // transition would continue. To propagate the + // error so that it'd be handled by the `error` + // hook, you would have to either + return Ember.RSVP.reject(e); + // or + throw e; + }); + } + } + }); + ``` + + @param {Transition} transition + @return {Promise} if the value returned from this hook is + a promise, the transition will pause until the transition + resolves. Otherwise, non-promise return values are not + utilized in any way. + */ + beforeModel: Ember.K, + + /** + This hook is called after this route's model has resolved. + It follows identical async/promise semantics to `beforeModel` + but is provided the route's resolved model in addition to + the `transition`, and is therefore suited to performing + logic that can only take place after the model has already + resolved. + + ```js + App.PostRoute = Ember.Route.extend({ + afterModel: function(posts, transition) { + if (posts.length === 1) { + this.transitionTo('post.show', posts[0]); + } + } + }); + ``` + + Refer to documentation for `beforeModel` for a description + of transition-pausing semantics when a promise is returned + from this hook. + + @param {Transition} transition + @return {Promise} if the value returned from this hook is + a promise, the transition will pause until the transition + resolves. Otherwise, non-promise return values are not + utilized in any way. + */ + afterModel: function(resolvedModel, transition) { + this.redirect(resolvedModel, transition); + }, + + + /** + @private + + Called when the context is changed by router.js. + + @method contextDidChange + */ + contextDidChange: function() { + this.currentModel = this.context; + }, + + /** + A hook you can implement to convert the URL into the model for + this route. + + ```js + App.Router.map(function() { + this.resource('post', {path: '/posts/:post_id'}); + }); + ``` + + The model for the `post` route is `App.Post.find(params.post_id)`. + + By default, if your route has a dynamic segment ending in `_id`: + + * The model class is determined from the segment (`post_id`'s + class is `App.Post`) + * The find method is called on the model class with the value of + the dynamic segment. + + Note that for routes with dynamic segments, this hook is only + executed when entered via the URL. If the route is entered + through a transition (e.g. when using the `linkTo` Handlebars + helper), then a model context is already provided and this hook + is not called. Routes without dynamic segments will always + execute the model hook. + + This hook follows the asynchronous/promise semantics + described in the documentation for `beforeModel`. In particular, + if a promise returned from `model` fails, the error will be + handled by the `error` hook on `Ember.Route`. + + @method model + @param {Object} params the parameters extracted from the URL + */ + model: function(params, resolvedParentModels) { + var match, name, sawParams, value; + + for (var prop in params) { + if (match = prop.match(/^(.*)_id$/)) { + name = match[1]; + value = params[prop]; + } + sawParams = true; + } + + if (!name && sawParams) { return params; } + else if (!name) { return; } + + var className = classify(name), + namespace = this.router.namespace, + modelClass = namespace[className]; + + Ember.assert("You used the dynamic segment " + name + "_id in your router, but " + namespace + "." + className + " did not exist and you did not override your route's `model` hook.", modelClass); + return modelClass.find(value); + }, + + /** + A hook you can implement to convert the route's model into parameters + for the URL. + + ```js + App.Router.map(function() { + this.resource('post', {path: '/posts/:post_id'}); + }); + + App.PostRoute = Ember.Route.extend({ + model: function(params) { + // the server returns `{ id: 12 }` + return jQuery.getJSON("/posts/" + params.post_id); + }, + + serialize: function(model) { + // this will make the URL `/posts/12` + return { post_id: model.id }; + } + }); + ``` + + The default `serialize` method inserts the model's `id` into the + route's dynamic segment (in this case, `:post_id`). + + This method is called when `transitionTo` is called with a context + in order to populate the URL. + + @method serialize + @param {Object} model the route's model + @param {Array} params an Array of parameter names for the current + route (in the example, `['post_id']`. + @return {Object} the serialized parameters + */ + serialize: function(model, params) { + if (params.length !== 1) { return; } + + var name = params[0], object = {}; + + if (/_id$/.test(name)) { + object[name] = get(model, 'id'); + } else { + object[name] = model; + } + + return object; + }, + + /** + A hook you can use to setup the controller for the current route. + + This method is called with the controller for the current route and the + model supplied by the `model` hook. + + By default, the `setupController` hook sets the `content` property of + the controller to the `model`. + + This means that your template will get a proxy for the model as its + context, and you can act as though the model itself was the context. + + The provided controller will be one resolved based on the name + of this route. + + If no explicit controller is defined, Ember will automatically create + an appropriate controller for the model. + + * if the model is an `Ember.Array` (including record arrays from Ember + Data), the controller is an `Ember.ArrayController`. + * otherwise, the controller is an `Ember.ObjectController`. + + As an example, consider the router: + + ```js + App.Router.map(function() { + this.resource('post', {path: '/posts/:post_id'}); + }); + ``` + + For the `post` route, a controller named `App.PostController` would + be used if it is defined. If it is not defined, an `Ember.ObjectController` + instance would be used. + + @method setupController + */ + setupController: function(controller, context){ + if (controller && (context !== undefined)) { + set(controller, 'model', context); + } + }, + + /** + Returns the controller for a particular route. + + ```js + App.PostRoute = Ember.Route.extend({ + setupController: function(controller, post) { + this._super(controller, post); + this.controllerFor('posts').set('currentPost', post); + } + }); + ``` + + By default, the controller for `post` is the shared instance of + `App.PostController`. + + @method controllerFor + @param {String} name the name of the route + @param {Object} model the model associated with the route (optional) + @return {Ember.Controller} + */ + controllerFor: function(name, model) { + var container = this.router.container, + controller = container.lookup('controller:' + name); + + if (!controller) { + model = model || this.modelFor(name); + + Ember.assert("You are trying to look up a controller that you did not define, and for which Ember does not know the model.\n\nThis is not a controller for a route, so you must explicitly define the controller ("+this.router.namespace.toString() + "." + Ember.String.capitalize(Ember.String.camelize(name))+"Controller) or pass a model as the second parameter to `controllerFor`, so that Ember knows which type of controller to create for you.", model || this.container.lookup('route:' + name)); + + controller = Ember.generateController(container, name, model); + } + + return controller; + }, + + /** + Returns the current model for a given route. + + This is the object returned by the `model` hook of the route + in question. + + @method modelFor + @param {String} name the name of the route + @return {Object} the model object + */ + modelFor: function(name) { + + var route = this.container.lookup('route:' + name), + transition = this.router.router.activeTransition; + + // If we are mid-transition, we want to try and look up + // resolved parent contexts on the current transitionEvent. + if (transition) { + var modelLookupName = (route && route.routeName) || name; + if (transition.resolvedModels.hasOwnProperty(modelLookupName)) { + return transition.resolvedModels[modelLookupName]; + } + } + + return route && route.currentModel; + }, + + /** + A hook you can use to render the template for the current route. + + This method is called with the controller for the current route and the + model supplied by the `model` hook. By default, it renders the route's + template, configured with the controller for the route. + + This method can be overridden to set up and render additional or + alternative templates. + + @method renderTemplate + @param {Object} controller the route's controller + @param {Object} model the route's model + */ + renderTemplate: function(controller, model) { + this.render(); + }, + + /** + Renders a template into an outlet. + + This method has a number of defaults, based on the name of the + route specified in the router. + + For example: + + ```js + App.Router.map(function() { + this.route('index'); + this.resource('post', {path: '/posts/:post_id'}); + }); + + App.PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render(); + } + }); + ``` + + The name of the `PostRoute`, as defined by the router, is `post`. + + By default, render will: + + * render the `post` template + * with the `post` view (`PostView`) for event handling, if one exists + * and the `post` controller (`PostController`), if one exists + * into the `main` outlet of the `application` template + + You can override this behavior: + + ```js + App.PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render('myPost', { // the template to render + into: 'index', // the template to render into + outlet: 'detail', // the name of the outlet in that template + controller: 'blogPost' // the controller to use for the template + }); + } + }); + ``` + + Remember that the controller's `content` will be the route's model. In + this case, the default model will be `App.Post.find(params.post_id)`. + + @method render + @param {String} name the name of the template to render + @param {Object} options the options + */ + render: function(name, options) { + Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !Ember.isNone(arguments[0]) : true); + + if (typeof name === 'object' && !options) { + options = name; + name = this.routeName; + } + + name = name ? name.replace(/\//g, '.') : this.routeName; + + var container = this.container, + view = container.lookup('view:' + name), + template = container.lookup('template:' + name); + + if (!view && !template) { + if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { + Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); + } + return; + } + + options = normalizeOptions(this, name, template, options); + view = setupView(view, container, options); + + if (options.outlet === 'main') { this.lastRenderedTemplate = name; } + + appendView(this, view, options); + }, + + willDestroy: function() { + this.teardownViews(); + }, + + teardownViews: function() { + // Tear down the top level view + if (this.teardownTopLevelView) { this.teardownTopLevelView(); } + + // Tear down any outlets rendered with 'into' + var teardownOutletViews = this.teardownOutletViews || []; + a_forEach(teardownOutletViews, function(teardownOutletView) { + teardownOutletView(); + }); + + delete this.teardownTopLevelView; + delete this.teardownOutletViews; + delete this.lastRenderedTemplate; + } +}); + +function parentRoute(route) { + var handlerInfos = route.router.router.targetHandlerInfos; + + if (!handlerInfos) { return; } + + var parent, current; + + for (var i=0, l=handlerInfos.length; i<l; i++) { + current = handlerInfos[i].handler; + if (current === route) { return parent; } + parent = current; + } +} + +function parentTemplate(route, isRecursive) { + var parent = parentRoute(route), template; + + if (!parent) { return; } + + Ember.warn(fmt("The immediate parent route ('%@') did not render into the main outlet and the default 'into' option ('%@') may not be expected", [get(parent, 'routeName'), get(route, 'routeName')]), !isRecursive); + + if (template = parent.lastRenderedTemplate) { + return template; + } else { + return parentTemplate(parent, true); + } +} + +function normalizeOptions(route, name, template, options) { + options = options || {}; + options.into = options.into ? options.into.replace(/\//g, '.') : parentTemplate(route); + options.outlet = options.outlet || 'main'; + options.name = name; + options.template = template; + options.LOG_VIEW_LOOKUPS = get(route.router, 'namespace.LOG_VIEW_LOOKUPS'); + + Ember.assert("An outlet ("+options.outlet+") was specified but this view will render at the root level.", options.outlet === 'main' || options.into); + + var controller = options.controller, namedController; + + if (options.controller) { + controller = options.controller; + } else if (namedController = route.container.lookup('controller:' + name)) { + controller = namedController; + } else { + controller = route.routeName; + } + + if (typeof controller === 'string') { + controller = route.container.lookup('controller:' + controller); + } + + options.controller = controller; + + return options; +} + +function setupView(view, container, options) { + if (view) { + if (options.LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + options.name + " with " + view, { fullName: 'view:' + options.name }); + } + } else { + var defaultView = options.into ? 'view:default' : 'view:toplevel'; + view = container.lookup(defaultView); + if (options.LOG_VIEW_LOOKUPS) { + Ember.Logger.info("Rendering " + options.name + " with default view " + view, { fullName: 'view:' + options.name }); + } + } + + if (!get(view, 'templateName')) { + set(view, 'template', options.template); + + set(view, '_debugTemplateName', options.name); + } + + set(view, 'renderedName', options.name); + set(view, 'controller', options.controller); + + return view; +} + +function appendView(route, view, options) { + if (options.into) { + var parentView = route.router._lookupActiveView(options.into); + var teardownOutletView = generateOutletTeardown(parentView, options.outlet); + if (!route.teardownOutletViews) { route.teardownOutletViews = []; } + a_replace(route.teardownOutletViews, 0, 0, [teardownOutletView]); + parentView.connectOutlet(options.outlet, view); + } else { + var rootElement = get(route, 'router.namespace.rootElement'); + // tear down view if one is already rendered + if (route.teardownTopLevelView) { + route.teardownTopLevelView(); + } + route.router._connectActiveView(options.name, view); + route.teardownTopLevelView = generateTopLevelTeardown(view); + view.appendTo(rootElement); + } +} + +function generateTopLevelTeardown(view) { + return function() { view.destroy(); }; +} + +function generateOutletTeardown(parentView, outlet) { + return function() { parentView.disconnectOutlet(outlet); }; +} + +})(); + + + +(function() { + +})(); + + + +(function() { +Ember.onLoad('Ember.Handlebars', function() { + var handlebarsResolve = Ember.Handlebars.resolveParams, + map = Ember.ArrayPolyfills.map, + get = Ember.get; + + function resolveParams(context, params, options) { + var resolved = handlebarsResolve(context, params, options); + return map.call(resolved, unwrap); + + function unwrap(object, i) { + if (params[i] === 'controller') { return object; } + + if (Ember.ControllerMixin.detect(object)) { + return unwrap(get(object, 'model')); + } else { + return object; + } + } + } + + Ember.Router.resolveParams = resolveParams; +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; +Ember.onLoad('Ember.Handlebars', function(Handlebars) { + + var resolveParams = Ember.Router.resolveParams, + isSimpleClick = Ember.ViewUtils.isSimpleClick; + + function fullRouteName(router, name) { + if (!router.hasRoute(name)) { + name = name + '.index'; + } + + return name; + } + + function resolvedPaths(options) { + var types = options.options.types.slice(1), + data = options.options.data; + + return resolveParams(options.context, options.params, { types: types, data: data }); + } + + function args(linkView, router, route) { + var passedRouteName = route || linkView.namedRoute, routeName; + + routeName = fullRouteName(router, passedRouteName); + + Ember.assert(fmt("The attempt to linkTo route '%@' failed. The router did not find '%@' in its possible routes: '%@'", [passedRouteName, passedRouteName, Ember.keys(router.router.recognizer.names).join("', '")]), router.hasRoute(routeName)); + + var ret = [ routeName ]; + return ret.concat(resolvedPaths(linkView.parameters)); + } + + /** + `Ember.LinkView` renders an element whose `click` event triggers a + transition of the application's instance of `Ember.Router` to + a supplied route by name. + + Instances of `LinkView` will most likely be created through + the `linkTo` Handlebars helper, but properties of this class + can be overridden to customize application-wide behavior. + + @class LinkView + @namespace Ember + @extends Ember.View + @see {Handlebars.helpers.linkTo} + **/ + var LinkView = Ember.LinkView = Ember.View.extend({ + tagName: 'a', + namedRoute: null, + currentWhen: null, + + /** + Sets the `title` attribute of the `LinkView`'s HTML element. + + @property title + @default null + **/ + title: null, + + /** + The CSS class to apply to `LinkView`'s element when its `active` + property is `true`. + + @property activeClass + @type String + @default active + **/ + activeClass: 'active', + + /** + The CSS class to apply to a `LinkView`'s element when its `disabled` + property is `true`. + + @property disabledClass + @type String + @default disabled + **/ + disabledClass: 'disabled', + _isDisabled: false, + + /** + Determines whether the `LinkView` will trigger routing via + the `replaceWith` routing strategy. + + @type Boolean + @default false + **/ + replace: false, + attributeBindings: ['href', 'title'], + classNameBindings: ['active', 'disabled'], + + /** + By default the `{{linkTo}}` helper responds to the `click` event. You + can override this globally by setting this property to your custom + event name. + + This is particularly useful on mobile when one wants to avoid the 300ms + click delay using some sort of custom `tap` event. + + @property eventName + @type String + @default click + */ + eventName: 'click', + + // this is doc'ed here so it shows up in the events + // section of the API documentation, which is where + // people will likely go looking for it. + /** + Triggers the `LinkView`'s routing behavior. If + `eventName` is changed to a value other than `click` + the routing behavior will trigger on that custom event + instead. + + @event click + **/ + + init: function() { + this._super(); + // Map desired event name to invoke function + var eventName = get(this, 'eventName'); + this.on(eventName, this, this._invoke); + }, + + /** + @private + + Even though this isn't a virtual view, we want to treat it as if it is + so that you can access the parent with {{view.prop}} + + @method concreteView + **/ + concreteView: Ember.computed(function() { + return get(this, 'parentView'); + }).property('parentView'), + + /** + + Accessed as a classname binding to apply the `LinkView`'s `disabledClass` + CSS `class` to the element when the link is disabled. + + When `true` interactions with the element will not trigger route changes. + @property disabled + */ + disabled: Ember.computed(function(key, value) { + if (value !== undefined) { this.set('_isDisabled', value); } + + return value ? this.get('disabledClass') : false; + }), + + /** + Accessed as a classname binding to apply the `LinkView`'s `activeClass` + CSS `class` to the element when the link is active. + + A `LinkView` is considered active when its `currentWhen` property is `true` + or the application's current route is the route the `LinkView` would trigger + transitions into. + + @property active + **/ + active: Ember.computed(function() { + var router = this.get('router'), + params = resolvedPaths(this.parameters), + currentWithIndex = this.currentWhen + '.index', + isActive = router.isActive.apply(router, [this.currentWhen].concat(params)) || + router.isActive.apply(router, [currentWithIndex].concat(params)); + + if (isActive) { return get(this, 'activeClass'); } + }).property('namedRoute', 'router.url'), + + router: Ember.computed(function() { + return this.get('controller').container.lookup('router:main'); + }), + + /** + @private + + Event handler that invokes the link, activating the associated route. + + @method _invoke + @param {Event} event + */ + _invoke: function(event) { + if (!isSimpleClick(event)) { return true; } + + event.preventDefault(); + if (this.bubbles === false) { event.stopPropagation(); } + + if (get(this, '_isDisabled')) { return false; } + + var router = this.get('router'), + routeArgs = args(this, router); + + if (this.get('replace')) { + router.replaceWith.apply(router, routeArgs); + } else { + router.transitionTo.apply(router, routeArgs); + } + }, + + /** + Sets the element's `href` attribute to the url for + the `LinkView`'s targeted route. + + If the `LinkView`'s `tagName` is changed to a value other + than `a`, this property will be ignored. + + @property href + **/ + href: Ember.computed(function() { + if (this.get('tagName') !== 'a') { return false; } + + var router = this.get('router'); + return router.generate.apply(router, args(this, router)); + }) + }); + + LinkView.toString = function() { return "LinkView"; }; + + /** + The `{{linkTo}}` helper renders a link to the supplied + `routeName` passing an optionally supplied model to the + route as its `model` context of the route. The block + for `{{linkTo}}` becomes the innerHTML of the rendered + element: + + ```handlebars + {{#linkTo photoGallery}} + Great Hamster Photos + {{/linkTo}} + ``` + + ```html + <a href="/hamster-photos"> + Great Hamster Photos + </a> + ``` + + ### Supplying a tagName + By default `{{linkTo}}` renders an `<a>` element. This can + be overridden for a single use of `{{linkTo}}` by supplying + a `tagName` option: + + ```handlebars + {{#linkTo photoGallery tagName="li"}} + Great Hamster Photos + {{/linkTo}} + ``` + + ```html + <li> + Great Hamster Photos + </li> + ``` + + To override this option for your entire application, see + "Overriding Application-wide Defaults". + + ### Handling `href` + `{{linkTo}}` will use your application's Router to + fill the element's `href` property with a url that + matches the path to the supplied `routeName` for your + routers's configured `Location` scheme, which defaults + to Ember.HashLocation. + + ### Handling current route + `{{linkTo}}` will apply a CSS class name of 'active' + when the application's current route matches + the supplied routeName. For example, if the application's + current route is 'photoGallery.recent' the following + use of `{{linkTo}}`: + + ```handlebars + {{#linkTo photoGallery.recent}} + Great Hamster Photos from the last week + {{/linkTo}} + ``` + + will result in + + ```html + <a href="/hamster-photos/this-week" class="active"> + Great Hamster Photos + </a> + ``` + + The CSS class name used for active classes can be customized + for a single use of `{{linkTo}}` by passing an `activeClass` + option: + + ```handlebars + {{#linkTo photoGallery.recent activeClass="current-url"}} + Great Hamster Photos from the last week + {{/linkTo}} + ``` + + ```html + <a href="/hamster-photos/this-week" class="current-url"> + Great Hamster Photos + </a> + ``` + + To override this option for your entire application, see + "Overriding Application-wide Defaults". + + ### Supplying a model + An optional model argument can be used for routes whose + paths contain dynamic segments. This argument will become + the model context of the linked route: + + ```javascript + App.Router.map(function(){ + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }) + ``` + + ```handlebars + {{#linkTo photoGallery aPhoto}} + {{aPhoto.title}} + {{/linkTo}} + ``` + + ```html + <a href="/hamster-photos/42"> + Tomster + </a> + ``` + + ### Supplying multiple models + For deep-linking to route paths that contain multiple + dynamic segments, multiple model arguments can be used. + As the router transitions through the route path, each + supplied model argument will become the context for the + route with the dynamic segments: + + ```javascript + App.Router.map(function(){ + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}, function(){ + this.route("comment", {path: "comments/:comment_id"}); + }); + }); + ``` + This argument will become the model context of the linked route: + + ```handlebars + {{#linkTo photoGallery.comment aPhoto comment}} + {{comment.body}} + {{/linkTo}} + ``` + + ```html + <a href="/hamster-photos/42/comment/718"> + A+++ would snuggle again. + </a> + ``` + + ### Overriding Application-wide Defaults + ``{{linkTo}}`` creates an instance of Ember.LinkView + for rendering. To override options for your entire + application, reopen Ember.LinkView and supply the + desired values: + + ``` javascript + Ember.LinkView.reopen({ + activeClass: "is-active", + tagName: 'li' + }) + ``` + + It is also possible to override the default event in + this manner: + + ``` javascript + Ember.LinkView.reopen({ + eventName: 'customEventName' + }); + ``` + + @method linkTo + @for Ember.Handlebars.helpers + @param {String} routeName + @param {Object} [context]* + @return {String} HTML string + */ + Ember.Handlebars.registerHelper('linkTo', function(name) { + var options = [].slice.call(arguments, -1)[0]; + var params = [].slice.call(arguments, 1, -1); + + var hash = options.hash; + + hash.namedRoute = name; + hash.currentWhen = hash.currentWhen || name; + hash.disabledBinding = hash.disabledWhen; + + hash.parameters = { + context: this, + options: options, + params: params + }; + + return Ember.Handlebars.helpers.view.call(this, LinkView, options); + }); + +}); + + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; +Ember.onLoad('Ember.Handlebars', function(Handlebars) { + /** + @module ember + @submodule ember-routing + */ + + Handlebars.OutletView = Ember.ContainerView.extend(Ember._Metamorph); + + /** + The `outlet` helper is a placeholder that the router will fill in with + the appropriate template based on the current state of the application. + + ``` handlebars + {{outlet}} + ``` + + By default, a template based on Ember's naming conventions will be rendered + into the `outlet` (e.g. `App.PostsRoute` will render the `posts` template). + + You can render a different template by using the `render()` method in the + route's `renderTemplate` hook. The following will render the `favoritePost` + template into the `outlet`. + + ``` javascript + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function() { + this.render('favoritePost'); + } + }); + ``` + + You can create custom named outlets for more control. + + ``` handlebars + {{outlet favoritePost}} + {{outlet posts}} + ``` + + Then you can define what template is rendered into each outlet in your + route. + + + ``` javascript + App.PostsRoute = Ember.Route.extend({ + renderTemplate: function() { + this.render('favoritePost', { outlet: 'favoritePost' }); + this.render('posts', { outlet: 'posts' }); + } + }); + ``` + + You can specify the view class that the outlet uses to contain and manage the + templates rendered into it. + + ``` handlebars + {{outlet viewClass=App.SectionContainer}} + ``` + + ``` javascript + App.SectionContainer = Ember.ContainerView.extend({ + tagName: 'section', + classNames: ['special'] + }); + ``` + + @method outlet + @for Ember.Handlebars.helpers + @param {String} property the property on the controller + that holds the view for this outlet + */ + Handlebars.registerHelper('outlet', function(property, options) { + var outletSource, outletContainerClass; + + if (property && property.data && property.data.isRenderData) { + options = property; + property = 'main'; + } + + outletSource = options.data.view; + while (!(outletSource.get('template.isTop'))){ + outletSource = outletSource.get('_parentView'); + } + + outletContainerClass = options.hash.viewClass || Handlebars.OutletView; + + options.data.view.set('outletSource', outletSource); + options.hash.currentViewBinding = '_view.outletSource._outlets.' + property; + + return Handlebars.helpers.view.call(this, outletContainerClass, options); + }); +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; +Ember.onLoad('Ember.Handlebars', function(Handlebars) { + + /** + Renders the named template in the current context with the same-named + controller. + + If a view class with the same name exists, the view class will be used. + + The optional second argument is a property path that will be bound + to the `model` property of the controller. + + If a `model` property path is specified, then a new instance of the + controller will be created. + + If no `model` property path is provided, then the helper will use the + singleton instance of the controller. A given controller may only be used + one time in your app in this manner. + + The default target for `{{action}}`s in the rendered template is the + controller. + + @method render + @for Ember.Handlebars.helpers + @param {String} name + @param {Object?} contextString + @param {Hash} options + */ + Ember.Handlebars.registerHelper('render', function(name, contextString, options) { + Ember.assert("You must pass a template to render", arguments.length >= 2); + var container, router, controller, view, context, lookupOptions; + + if (arguments.length === 2) { + options = contextString; + contextString = undefined; + } + + if (typeof contextString === 'string') { + context = Ember.Handlebars.get(options.contexts[1], contextString, options); + lookupOptions = { singleton: false }; + } + + name = name.replace(/\//g, '.'); + container = options.data.keywords.controller.container; + router = container.lookup('router:main'); + + Ember.assert("You can only use the {{render}} helper once without a model object as its second argument, as in {{render \"post\" post}}.", context || !router || !router._lookupActiveView(name)); + + view = container.lookup('view:' + name) || container.lookup('view:default'); + + var controllerName = options.hash.controller; + + // Look up the controller by name, if provided. + if (controllerName) { + controller = container.lookup('controller:' + controllerName, lookupOptions); + Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", !!controller); + } else { + controller = Ember.controllerFor(container, name, context, lookupOptions); + } + + if (controller && context) { + controller.set('model', context); + } + + var root = options.contexts[1]; + + if (root) { + view.registerObserver(root, contextString, function() { + controller.set('model', Ember.Handlebars.get(root, contextString, options)); + }); + } + + controller.set('target', options.data.keywords.controller); + + options.hash.viewName = Ember.String.camelize(name); + options.hash.template = container.lookup('template:' + name); + options.hash.controller = controller; + + if (router && !context) { + router._connectActiveView(name, view); + } + + Ember.Handlebars.helpers.view.call(this, view, options); + }); + +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ +Ember.onLoad('Ember.Handlebars', function(Handlebars) { + + var resolveParams = Ember.Router.resolveParams, + isSimpleClick = Ember.ViewUtils.isSimpleClick; + + var EmberHandlebars = Ember.Handlebars, + handlebarsGet = EmberHandlebars.get, + SafeString = EmberHandlebars.SafeString, + forEach = Ember.ArrayPolyfills.forEach, + get = Ember.get, + a_slice = Array.prototype.slice; + + function args(options, actionName) { + var ret = []; + if (actionName) { ret.push(actionName); } + + var types = options.options.types.slice(1), + data = options.options.data; + + return ret.concat(resolveParams(options.context, options.params, { types: types, data: data })); + } + + var ActionHelper = EmberHandlebars.ActionHelper = { + registeredActions: {} + }; + + var keys = ["alt", "shift", "meta", "ctrl"]; + + var isAllowedClick = function(event, allowedKeys) { + if (typeof allowedKeys === "undefined") { + return isSimpleClick(event); + } + + var allowed = true; + + forEach.call(keys, function(key) { + if (event[key + "Key"] && allowedKeys.indexOf(key) === -1) { + allowed = false; + } + }); + + return allowed; + }; + + ActionHelper.registerAction = function(actionName, options, allowedKeys) { + var actionId = (++Ember.uuid).toString(); + + ActionHelper.registeredActions[actionId] = { + eventName: options.eventName, + handler: function(event) { + if (!isAllowedClick(event, allowedKeys)) { return true; } + + event.preventDefault(); + + if (options.bubbles === false) { + event.stopPropagation(); + } + + var target = options.target; + + if (target.target) { + target = handlebarsGet(target.root, target.target, target.options); + } else { + target = target.root; + } + + Ember.run(function() { + if (target.send) { + target.send.apply(target, args(options.parameters, actionName)); + } else { + Ember.assert("The action '" + actionName + "' did not exist on " + target, typeof target[actionName] === 'function'); + target[actionName].apply(target, args(options.parameters)); + } + }); + } + }; + + options.view.on('willClearRender', function() { + delete ActionHelper.registeredActions[actionId]; + }); + + return actionId; + }; + + /** + The `{{action}}` helper registers an HTML element within a template for DOM + event handling and forwards that interaction to the view's controller + or supplied `target` option (see 'Specifying a Target'). + + If the view's controller does not implement the event, the event is sent + to the current route, and it bubbles up the route hierarchy from there. + + User interaction with that element will invoke the supplied action name on + the appropriate target. + + Given the following Handlebars template on the page + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + <div {{action anActionName}}> + click me + </div> + </script> + ``` + + And application code + + ```javascript + AController = Ember.Controller.extend({ + anActionName: function() {} + }); + + AView = Ember.View.extend({ + controller: AController.create(), + templateName: 'a-template' + }); + + aView = AView.create(); + aView.appendTo('body'); + ``` + + Will result in the following rendered HTML + + ```html + <div class="ember-view"> + <div data-ember-action="1"> + click me + </div> + </div> + ``` + + Clicking "click me" will trigger the `anActionName` method of the + `AController`. In this case, no additional parameters will be passed. + + If you provide additional parameters to the helper: + + ```handlebars + <button {{action 'edit' post}}>Edit</button> + ``` + + Those parameters will be passed along as arguments to the JavaScript + function implementing the action. + + ### Event Propagation + + Events triggered through the action helper will automatically have + `.preventDefault()` called on them. You do not need to do so in your event + handlers. + + To also disable bubbling, pass `bubbles=false` to the helper: + + ```handlebars + <button {{action 'edit' post bubbles=false}}>Edit</button> + ``` + + If you need the default handler to trigger you should either register your + own event handler, or use event methods on your view class. See `Ember.View` + 'Responding to Browser Events' for more information. + + ### Specifying DOM event type + + By default the `{{action}}` helper registers for DOM `click` events. You can + supply an `on` option to the helper to specify a different DOM event name: + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + <div {{action anActionName on="doubleClick"}}> + click me + </div> + </script> + ``` + + See `Ember.View` 'Responding to Browser Events' for a list of + acceptable DOM event names. + + NOTE: Because `{{action}}` depends on Ember's event dispatch system it will + only function if an `Ember.EventDispatcher` instance is available. An + `Ember.EventDispatcher` instance will be created when a new `Ember.Application` + is created. Having an instance of `Ember.Application` will satisfy this + requirement. + + ### Specifying whitelisted modifier keys + + By default the `{{action}}` helper will ignore click event with pressed modifier + keys. You can supply an `allowedKeys` option to specify which keys should not be ignored. + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + <div {{action anActionName allowedKeys="alt"}}> + click me + </div> + </script> + ``` + + This way the `{{action}}` will fire when clicking with the alt key pressed down. + + ### Specifying a Target + + There are several possible target objects for `{{action}}` helpers: + + In a typical Ember application, where views are managed through use of the + `{{outlet}}` helper, actions will bubble to the current controller, then + to the current route, and then up the route hierarchy. + + Alternatively, a `target` option can be provided to the helper to change + which object will receive the method call. This option must be a path + to an object, accessible in the current context: + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + <div {{action anActionName target="MyApplication.someObject"}}> + click me + </div> + </script> + ``` + + Clicking "click me" in the rendered HTML of the above template will trigger + the `anActionName` method of the object at `MyApplication.someObject`. + + If an action's target does not implement a method that matches the supplied + action name an error will be thrown. + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + <div {{action aMethodNameThatIsMissing}}> + click me + </div> + </script> + ``` + + With the following application code + + ```javascript + AView = Ember.View.extend({ + templateName; 'a-template', + // note: no method 'aMethodNameThatIsMissing' + anActionName: function(event) {} + }); + + aView = AView.create(); + aView.appendTo('body'); + ``` + + Will throw `Uncaught TypeError: Cannot call method 'call' of undefined` when + "click me" is clicked. + + ### Additional Parameters + + You may specify additional parameters to the `{{action}}` helper. These + parameters are passed along as the arguments to the JavaScript function + implementing the action. + + ```handlebars + <script type="text/x-handlebars" data-template-name='a-template'> + {{#each person in people}} + <div {{action edit person}}> + click me + </div> + {{/each}} + </script> + ``` + + Clicking "click me" will trigger the `edit` method on the current view's + controller with the current person as a parameter. + + @method action + @for Ember.Handlebars.helpers + @param {String} actionName + @param {Object} [context]* + @param {Hash} options + */ + EmberHandlebars.registerHelper('action', function(actionName) { + var options = arguments[arguments.length - 1], + contexts = a_slice.call(arguments, 1, -1); + + var hash = options.hash, + controller; + + // create a hash to pass along to registerAction + var action = { + eventName: hash.on || "click" + }; + + action.parameters = { + context: this, + options: options, + params: contexts + }; + + action.view = options.data.view; + + var root, target; + + if (hash.target) { + root = this; + target = hash.target; + } else if (controller = options.data.keywords.controller) { + root = controller; + } + + action.target = { root: root, target: target, options: options }; + action.bubbles = hash.bubbles; + + var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys); + return new SafeString('data-ember-action="' + actionId + '"'); + }); + +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +if (Ember.ENV.EXPERIMENTAL_CONTROL_HELPER) { + var get = Ember.get, set = Ember.set; + + /** + The control helper is currently under development and is considered experimental. + To enable it, set `ENV.EXPERIMENTAL_CONTROL_HELPER = true` before requiring Ember. + + @method control + @for Ember.Handlebars.helpers + @param {String} path + @param {String} modelPath + @param {Hash} options + @return {String} HTML string + */ + Ember.Handlebars.registerHelper('control', function(path, modelPath, options) { + if (arguments.length === 2) { + options = modelPath; + modelPath = undefined; + } + + var model; + + if (modelPath) { + model = Ember.Handlebars.get(this, modelPath, options); + } + + var controller = options.data.keywords.controller, + view = options.data.keywords.view, + children = get(controller, '_childContainers'), + controlID = options.hash.controlID, + container, subContainer; + + if (children.hasOwnProperty(controlID)) { + subContainer = children[controlID]; + } else { + container = get(controller, 'container'), + subContainer = container.child(); + children[controlID] = subContainer; + } + + var normalizedPath = path.replace(/\//g, '.'); + + var childView = subContainer.lookup('view:' + normalizedPath) || subContainer.lookup('view:default'), + childController = subContainer.lookup('controller:' + normalizedPath), + childTemplate = subContainer.lookup('template:' + path); + + Ember.assert("Could not find controller for path: " + normalizedPath, childController); + Ember.assert("Could not find view for path: " + normalizedPath, childView); + + set(childController, 'target', controller); + set(childController, 'model', model); + + options.hash.template = childTemplate; + options.hash.controller = childController; + + function observer() { + var model = Ember.Handlebars.get(this, modelPath, options); + set(childController, 'model', model); + childView.rerender(); + } + + if (modelPath) { + Ember.addObserver(this, modelPath, observer); + childView.one('willDestroyElement', this, function() { + Ember.removeObserver(this, modelPath, observer); + }); + } + + Ember.Handlebars.helpers.view.call(this, childView, options); + }); +} + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; + +Ember.ControllerMixin.reopen({ + /** + Transition the application into another route. The route may + be either a single route or route path: + + ```javascript + aController.transitionToRoute('blogPosts'); + aController.transitionToRoute('blogPosts.recentEntries'); + ``` + + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: + + ```javascript + aController.transitionToRoute('blogPost', aPost); + ``` + + @param {String} name the name of the route + @param {...Object} models the + @for Ember.ControllerMixin + @method transitionToRoute + */ + transitionToRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'), + method = target.transitionToRoute || target.transitionTo; + return method.apply(target, arguments); + }, + + /** + @deprecated + @for Ember.ControllerMixin + @method transitionTo + */ + transitionTo: function() { + Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); + return this.transitionToRoute.apply(this, arguments); + }, + + replaceRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'), + method = target.replaceRoute || target.replaceWith; + return method.apply(target, arguments); + }, + + /** + @deprecated + @for Ember.ControllerMixin + @method replaceWith + */ + replaceWith: function() { + Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); + return this.replaceRoute.apply(this, arguments); + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; + +Ember.View.reopen({ + init: function() { + set(this, '_outlets', {}); + this._super(); + }, + + connectOutlet: function(outletName, view) { + if (this._pendingDisconnections) { + delete this._pendingDisconnections[outletName]; + } + + if (this._hasEquivalentView(outletName, view)) { + view.destroy(); + return; + } + + var outlets = get(this, '_outlets'), + container = get(this, 'container'), + router = container && container.lookup('router:main'), + renderedName = get(view, 'renderedName'); + + set(outlets, outletName, view); + + if (router && renderedName) { + router._connectActiveView(renderedName, view); + } + }, + + _hasEquivalentView: function(outletName, view) { + var existingView = get(this, '_outlets.'+outletName); + return existingView && + existingView.constructor === view.constructor && + existingView.get('template') === view.get('template') && + existingView.get('context') === view.get('context'); + }, + + disconnectOutlet: function(outletName) { + if (!this._pendingDisconnections) { + this._pendingDisconnections = {}; + } + this._pendingDisconnections[outletName] = true; + Ember.run.once(this, '_finishDisconnections'); + }, + + _finishDisconnections: function() { + var outlets = get(this, '_outlets'); + var pendingDisconnections = this._pendingDisconnections; + this._pendingDisconnections = null; + + for (var outletName in pendingDisconnections) { + set(outlets, outletName, null); + } + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-views +*/ + +// Add a new named queue after the 'actions' queue (where RSVP promises +// resolve), which is used in router transitions to prevent unnecessary +// loading state entry if all context promises resolve on the +// 'actions' queue first. + +var queues = Ember.run.queues, + indexOf = Ember.ArrayPolyfills.indexOf; +queues.splice(indexOf.call(queues, 'actions') + 1, 0, 'routerTransitions'); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; + +/* + This file implements the `location` API used by Ember's router. + + That API is: + + getURL: returns the current URL + setURL(path): sets the current URL + replaceURL(path): replace the current URL (optional) + onUpdateURL(callback): triggers the callback when the URL changes + formatURL(url): formats `url` to be placed into `href` attribute + + Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + + TODO: This should perhaps be moved so that it's visible in the doc output. +*/ + +/** + Ember.Location returns an instance of the correct implementation of + the `location` API. + + You can pass it a `implementation` ('hash', 'history', 'none') to force a + particular implementation. + + @class Location + @namespace Ember + @static +*/ +Ember.Location = { + create: function(options) { + var implementation = options && options.implementation; + Ember.assert("Ember.Location.create: you must specify a 'implementation' option", !!implementation); + + var implementationClass = this.implementations[implementation]; + Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); + + return implementationClass.create.apply(implementationClass, arguments); + }, + + registerImplementation: function(name, implementation) { + this.implementations[name] = implementation; + }, + + implementations: {} +}; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; + +/** + Ember.NoneLocation does not interact with the browser. It is useful for + testing, or when you need to manage state with your Router, but temporarily + don't want it to muck with the URL (for example when you embed your + application in a larger page). + + @class NoneLocation + @namespace Ember + @extends Ember.Object +*/ +Ember.NoneLocation = Ember.Object.extend({ + path: '', + + getURL: function() { + return get(this, 'path'); + }, + + setURL: function(path) { + set(this, 'path', path); + }, + + onUpdateURL: function(callback) { + this.updateCallback = callback; + }, + + handleURL: function(url) { + set(this, 'path', url); + this.updateCallback(url); + }, + + formatURL: function(url) { + // The return value is not overly meaningful, but we do not want to throw + // errors when test code renders templates containing {{action href=true}} + // helpers. + return url; + } +}); + +Ember.Location.registerImplementation('none', Ember.NoneLocation); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; + +/** + Ember.HashLocation implements the location API using the browser's + hash. At present, it relies on a hashchange event existing in the + browser. + + @class HashLocation + @namespace Ember + @extends Ember.Object +*/ +Ember.HashLocation = Ember.Object.extend({ + + init: function() { + set(this, 'location', get(this, 'location') || window.location); + }, + + /** + @private + + Returns the current `location.hash`, minus the '#' at the front. + + @method getURL + */ + getURL: function() { + return get(this, 'location').hash.substr(1); + }, + + /** + @private + + Set the `location.hash` and remembers what was set. This prevents + `onUpdateURL` callbacks from triggering when the hash was set by + `HashLocation`. + + @method setURL + @param path {String} + */ + setURL: function(path) { + get(this, 'location').hash = path; + set(this, 'lastSetURL', path); + }, + + /** + @private + + Register a callback to be invoked when the hash changes. These + callbacks will execute when the user presses the back or forward + button, but not after `setURL` is invoked. + + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var self = this; + var guid = Ember.guidFor(this); + + Ember.$(window).on('hashchange.ember-location-'+guid, function() { + Ember.run(function() { + var path = location.hash.substr(1); + if (get(self, 'lastSetURL') === path) { return; } + + set(self, 'lastSetURL', null); + + callback(path); + }); + }); + }, + + /** + @private + + Given a URL, formats it to be placed into the page as part + of an element's `href` attribute. + + This is used, for example, when using the {{action}} helper + to generate a URL based on an event. + + @method formatURL + @param url {String} + */ + formatURL: function(url) { + return '#'+url; + }, + + willDestroy: function() { + var guid = Ember.guidFor(this); + + Ember.$(window).unbind('hashchange.ember-location-'+guid); + } +}); + +Ember.Location.registerImplementation('hash', Ember.HashLocation); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-routing +*/ + +var get = Ember.get, set = Ember.set; +var popstateFired = false; + +/** + Ember.HistoryLocation implements the location API using the browser's + history.pushState API. + + @class HistoryLocation + @namespace Ember + @extends Ember.Object +*/ +Ember.HistoryLocation = Ember.Object.extend({ + + init: function() { + set(this, 'location', get(this, 'location') || window.location); + this.initState(); + }, + + /** + @private + + Used to set state on first call to setURL + + @method initState + */ + initState: function() { + set(this, 'history', get(this, 'history') || window.history); + this.replaceState(this.formatURL(this.getURL())); + }, + + /** + Will be pre-pended to path upon state change + + @property rootURL + @default '/' + */ + rootURL: '/', + + /** + @private + + Returns the current `location.pathname` without rootURL + + @method getURL + */ + getURL: function() { + var rootURL = get(this, 'rootURL'), + url = get(this, 'location').pathname; + + rootURL = rootURL.replace(/\/$/, ''); + url = url.replace(rootURL, ''); + + return url; + }, + + /** + @private + + Uses `history.pushState` to update the url without a page reload. + + @method setURL + @param path {String} + */ + setURL: function(path) { + path = this.formatURL(path); + + if (this.getState() && this.getState().path !== path) { + this.pushState(path); + } + }, + + /** + @private + + Uses `history.replaceState` to update the url without a page reload + or history modification. + + @method replaceURL + @param path {String} + */ + replaceURL: function(path) { + path = this.formatURL(path); + + if (this.getState() && this.getState().path !== path) { + this.replaceState(path); + } + }, + + /** + @private + + Get the current `history.state` + + @method getState + */ + getState: function() { + return get(this, 'history').state; + }, + + /** + @private + + Pushes a new state + + @method pushState + @param path {String} + */ + pushState: function(path) { + get(this, 'history').pushState({ path: path }, null, path); + // used for webkit workaround + this._previousURL = this.getURL(); + }, + + /** + @private + + Replaces the current state + + @method replaceState + @param path {String} + */ + replaceState: function(path) { + get(this, 'history').replaceState({ path: path }, null, path); + // used for webkit workaround + this._previousURL = this.getURL(); + }, + + /** + @private + + Register a callback to be invoked whenever the browser + history changes, including using forward and back buttons. + + @method onUpdateURL + @param callback {Function} + */ + onUpdateURL: function(callback) { + var guid = Ember.guidFor(this), + self = this; + + Ember.$(window).on('popstate.ember-location-'+guid, function(e) { + // Ignore initial page load popstate event in Chrome + if(!popstateFired) { + popstateFired = true; + if (self.getURL() === self._previousURL) { return; } + } + callback(self.getURL()); + }); + }, + + /** + @private + + Used when using `{{action}}` helper. The url is always appended to the rootURL. + + @method formatURL + @param url {String} + */ + formatURL: function(url) { + var rootURL = get(this, 'rootURL'); + + if (url !== '') { + rootURL = rootURL.replace(/\/$/, ''); + } + + return rootURL + url; + }, + + willDestroy: function() { + var guid = Ember.guidFor(this); + + Ember.$(window).unbind('popstate.ember-location-'+guid); + } +}); + +Ember.Location.registerImplementation('history', Ember.HistoryLocation); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +Ember Routing + +@module ember +@submodule ember-routing +@requires ember-states +@requires ember-views +*/ + +})(); + +(function() { +function visit(vertex, fn, visited, path) { + var name = vertex.name, + vertices = vertex.incoming, + names = vertex.incomingNames, + len = names.length, + i; + if (!visited) { + visited = {}; + } + if (!path) { + path = []; + } + if (visited.hasOwnProperty(name)) { + return; + } + path.push(name); + visited[name] = true; + for (i = 0; i < len; i++) { + visit(vertices[names[i]], fn, visited, path); + } + fn(vertex, path); + path.pop(); +} + +function DAG() { + this.names = []; + this.vertices = {}; +} + +DAG.prototype.add = function(name) { + if (!name) { return; } + if (this.vertices.hasOwnProperty(name)) { + return this.vertices[name]; + } + var vertex = { + name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null + }; + this.vertices[name] = vertex; + this.names.push(name); + return vertex; +}; + +DAG.prototype.map = function(name, value) { + this.add(name).value = value; +}; + +DAG.prototype.addEdge = function(fromName, toName) { + if (!fromName || !toName || fromName === toName) { + return; + } + var from = this.add(fromName), to = this.add(toName); + if (to.incoming.hasOwnProperty(fromName)) { + return; + } + function checkCycle(vertex, path) { + if (vertex.name === toName) { + throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); + } + } + visit(from, checkCycle); + from.hasOutgoing = true; + to.incoming[fromName] = from; + to.incomingNames.push(fromName); +}; + +DAG.prototype.topsort = function(fn) { + var visited = {}, + vertices = this.vertices, + names = this.names, + len = names.length, + i, vertex; + for (i = 0; i < len; i++) { + vertex = vertices[names[i]]; + if (!vertex.hasOutgoing) { + visit(vertex, fn, visited); + } + } +}; + +DAG.prototype.addEdges = function(name, value, before, after) { + var i; + this.map(name, value); + if (before) { + if (typeof before === 'string') { + this.addEdge(name, before); + } else { + for (i = 0; i < before.length; i++) { + this.addEdge(name, before[i]); + } + } + } + if (after) { + if (typeof after === 'string') { + this.addEdge(after, name); + } else { + for (i = 0; i < after.length; i++) { + this.addEdge(after[i], name); + } + } + } +}; + +Ember.DAG = DAG; + +})(); + + + +(function() { +/** +@module ember +@submodule ember-application +*/ + +var get = Ember.get, + classify = Ember.String.classify, + capitalize = Ember.String.capitalize, + decamelize = Ember.String.decamelize; + +/** + The DefaultResolver defines the default lookup rules to resolve + container lookups before consulting the container for registered + items: + + * templates are looked up on `Ember.TEMPLATES` + * other names are looked up on the application after converting + the name. For example, `controller:post` looks up + `App.PostController` by default. + * there are some nuances (see examples below) + + ### How Resolving Works + + The container calls this object's `resolve` method with the + `fullName` argument. + + It first parses the fullName into an object using `parseName`. + + Then it checks for the presence of a type-specific instance + method of the form `resolve[Type]` and calls it if it exists. + For example if it was resolving 'template:post', it would call + the `resolveTemplate` method. + + Its last resort is to call the `resolveOther` method. + + The methods of this object are designed to be easy to override + in a subclass. For example, you could enhance how a template + is resolved like so: + + ```javascript + App = Ember.Application.create({ + resolver: Ember.DefaultResolver.extend({ + resolveTemplate: function(parsedName) { + var resolvedTemplate = this._super(parsedName); + if (resolvedTemplate) { return resolvedTemplate; } + return Ember.TEMPLATES['not_found']; + } + }) + }); + ``` + + Some examples of how names are resolved: + + ``` + 'template:post' //=> Ember.TEMPLATES['post'] + 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] + // OR + // Ember.TEMPLATES['blog_post'] + 'controller:post' //=> App.PostController + 'controller:posts.index' //=> App.PostsIndexController + 'controller:blog/post' //=> Blog.PostController + 'controller:basic' //=> Ember.Controller + 'route:post' //=> App.PostRoute + 'route:posts.index' //=> App.PostsIndexRoute + 'route:blog/post' //=> Blog.PostRoute + 'route:basic' //=> Ember.Route + 'view:post' //=> App.PostView + 'view:posts.index' //=> App.PostsIndexView + 'view:blog/post' //=> Blog.PostView + 'view:basic' //=> Ember.View + 'foo:post' //=> App.PostFoo + ``` + + @class DefaultResolver + @namespace Ember + @extends Ember.Object +*/ +Ember.DefaultResolver = Ember.Object.extend({ + /** + This will be set to the Application instance when it is + created. + + @property namespace + */ + namespace: null, + /** + This method is called via the container's resolver method. + It parses the provided `fullName` and then looks up and + returns the appropriate template or class. + + @method resolve + @param {String} fullName the lookup string + @return {Object} the resolved factory + */ + resolve: function(fullName) { + var parsedName = this.parseName(fullName), + typeSpecificResolveMethod = this[parsedName.resolveMethodName]; + if (typeSpecificResolveMethod) { + var resolved = typeSpecificResolveMethod.call(this, parsedName); + if (resolved) { return resolved; } + } + return this.resolveOther(parsedName); + }, + /** + Convert the string name of the form "type:name" to + a Javascript object with the parsed aspects of the name + broken out. + + @protected + @method parseName + */ + parseName: function(fullName) { + var nameParts = fullName.split(":"), + type = nameParts[0], fullNameWithoutType = nameParts[1], + name = fullNameWithoutType, + namespace = get(this, 'namespace'), + root = namespace; + + if (type !== 'template' && name.indexOf('/') !== -1) { + var parts = name.split('/'); + name = parts[parts.length - 1]; + var namespaceName = capitalize(parts.slice(0, -1).join('.')); + root = Ember.Namespace.byName(namespaceName); + + Ember.assert('You are looking for a ' + name + ' ' + type + ' in the ' + namespaceName + ' namespace, but the namespace could not be found', root); + } + + return { + fullName: fullName, + type: type, + fullNameWithoutType: fullNameWithoutType, + name: name, + root: root, + resolveMethodName: "resolve" + classify(type) + }; + }, + /** + Look up the template in Ember.TEMPLATES + + @protected + @method resolveTemplate + */ + resolveTemplate: function(parsedName) { + var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/'); + + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; + } + + templateName = decamelize(templateName); + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; + } + }, + /** + Given a parseName object (output from `parseName`), apply + the conventions expected by `Ember.Router` + + @protected + @method useRouterNaming + */ + useRouterNaming: function(parsedName) { + parsedName.name = parsedName.name.replace(/\./g, '_'); + if (parsedName.name === 'basic') { + parsedName.name = ''; + } + }, + /** + @protected + @method resolveController + */ + resolveController: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + @protected + @method resolveRoute + */ + resolveRoute: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + @protected + @method resolveView + */ + resolveView: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + Look up the specified object (from parsedName) on the appropriate + namespace (usually on the Application) + + @protected + @method resolveOther + */ + resolveOther: function(parsedName) { + var className = classify(parsedName.name) + classify(parsedName.type), + factory = get(parsedName.root, className); + if (factory) { return factory; } + } +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-application +*/ + +var get = Ember.get, set = Ember.set; + +function DeprecatedContainer(container) { + this._container = container; +} + +DeprecatedContainer.deprecate = function(method) { + return function() { + var container = this._container; + + Ember.deprecate('Using the defaultContainer is no longer supported. [defaultContainer#' + method + '] see: http://git.io/EKPpnA', false); + return container[method].apply(container, arguments); + }; +}; + +DeprecatedContainer.prototype = { + _container: null, + lookup: DeprecatedContainer.deprecate('lookup'), + resolve: DeprecatedContainer.deprecate('resolve'), + register: DeprecatedContainer.deprecate('register') +}; + +/** + An instance of `Ember.Application` is the starting point for every Ember + application. It helps to instantiate, initialize and coordinate the many + objects that make up your app. + + Each Ember app has one and only one `Ember.Application` object. In fact, the + very first thing you should do in your application is create the instance: + + ```javascript + window.App = Ember.Application.create(); + ``` + + Typically, the application object is the only global variable. All other + classes in your app should be properties on the `Ember.Application` instance, + which highlights its first role: a global namespace. + + For example, if you define a view class, it might look like this: + + ```javascript + App.MyView = Ember.View.extend(); + ``` + + By default, calling `Ember.Application.create()` will automatically initialize + your application by calling the `Ember.Application.initialize()` method. If + you need to delay initialization, you can call your app's `deferReadiness()` + method. When you are ready for your app to be initialized, call its + `advanceReadiness()` method. + + You can define a `ready` method on the `Ember.Application` instance, which + will be run by Ember when the application is initialized. + + Because `Ember.Application` inherits from `Ember.Namespace`, any classes + you create will have useful string representations when calling `toString()`. + See the `Ember.Namespace` documentation for more information. + + While you can think of your `Ember.Application` as a container that holds the + other classes in your application, there are several other responsibilities + going on under-the-hood that you may want to understand. + + ### Event Delegation + + Ember uses a technique called _event delegation_. This allows the framework + to set up a global, shared event listener instead of requiring each view to + do it manually. For example, instead of each view registering its own + `mousedown` listener on its associated element, Ember sets up a `mousedown` + listener on the `body`. + + If a `mousedown` event occurs, Ember will look at the target of the event and + start walking up the DOM node tree, finding corresponding views and invoking + their `mouseDown` method as it goes. + + `Ember.Application` has a number of default events that it listens for, as + well as a mapping from lowercase events to camel-cased view method names. For + example, the `keypress` event causes the `keyPress` method on the view to be + called, the `dblclick` event causes `doubleClick` to be called, and so on. + + If there is a browser event that Ember does not listen for by default, you + can specify custom events and their corresponding view method names by + setting the application's `customEvents` property: + + ```javascript + App = Ember.Application.create({ + customEvents: { + // add support for the loadedmetadata media + // player event + 'loadedmetadata': "loadedMetadata" + } + }); + ``` + + By default, the application sets up these event listeners on the document + body. However, in cases where you are embedding an Ember application inside + an existing page, you may want it to set up the listeners on an element + inside the body. + + For example, if only events inside a DOM element with the ID of `ember-app` + should be delegated, set your application's `rootElement` property: + + ```javascript + window.App = Ember.Application.create({ + rootElement: '#ember-app' + }); + ``` + + The `rootElement` can be either a DOM element or a jQuery-compatible selector + string. Note that *views appended to the DOM outside the root element will + not receive events.* If you specify a custom root element, make sure you only + append views inside it! + + To learn more about the advantages of event delegation and the Ember view + layer, and a list of the event listeners that are setup by default, visit the + [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation). + + ### Initializers + + Libraries on top of Ember can register additional initializers, like so: + + ```javascript + Ember.Application.initializer({ + name: "store", + + initialize: function(container, application) { + container.register('store:main', application.Store); + } + }); + ``` + + ### Routing + + In addition to creating your application's router, `Ember.Application` is + also responsible for telling the router when to start routing. Transitions + between routes can be logged with the LOG_TRANSITIONS flag, and more + detailed intra-transition logging can be logged with + the LOG_TRANSITIONS_INTERNAL flag: + + ```javascript + window.App = Ember.Application.create({ + LOG_TRANSITIONS: true, // basic logging of successful transitions + LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps + }); + ``` + + By default, the router will begin trying to translate the current URL into + application state once the browser emits the `DOMContentReady` event. If you + need to defer routing, you can call the application's `deferReadiness()` + method. Once routing can begin, call the `advanceReadiness()` method. + + If there is any setup required before routing begins, you can implement a + `ready()` method on your app that will be invoked immediately before routing + begins. + ``` + + @class Application + @namespace Ember + @extends Ember.Namespace +*/ + +var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin, { + + /** + The root DOM element of the Application. This can be specified as an + element or a + [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). + + This is the element that will be passed to the Application's, + `eventDispatcher`, which sets up the listeners for event delegation. Every + view in your application should be a child of the element you specify here. + + @property rootElement + @type DOMElement + @default 'body' + */ + rootElement: 'body', + + /** + The `Ember.EventDispatcher` responsible for delegating events to this + application's views. + + The event dispatcher is created by the application at initialization time + and sets up event listeners on the DOM element described by the + application's `rootElement` property. + + See the documentation for `Ember.EventDispatcher` for more information. + + @property eventDispatcher + @type Ember.EventDispatcher + @default null + */ + eventDispatcher: null, + + /** + The DOM events for which the event dispatcher should listen. + + By default, the application's `Ember.EventDispatcher` listens + for a set of standard DOM events, such as `mousedown` and + `keyup`, and delegates them to your application's `Ember.View` + instances. + + If you would like additional events to be delegated to your + views, set your `Ember.Application`'s `customEvents` property + to a hash containing the DOM event name as the key and the + corresponding view method name as the value. For example: + + ```javascript + App = Ember.Application.create({ + customEvents: { + // add support for the loadedmetadata media + // player event + 'loadedmetadata': "loadedMetadata" + } + }); + ``` + + @property customEvents + @type Object + @default null + */ + customEvents: null, + + // Start off the number of deferrals at 1. This will be + // decremented by the Application's own `initialize` method. + _readinessDeferrals: 1, + + init: function() { + if (!this.$) { this.$ = Ember.$; } + this.__container__ = this.buildContainer(); + + this.Router = this.Router || this.defaultRouter(); + if (this.Router) { this.Router.namespace = this; } + + this._super(); + + this.scheduleInitialize(); + + if ( Ember.LOG_VERSION ) { + Ember.LOG_VERSION = false; // we only need to see this once per Application#init + Ember.debug('-------------------------------'); + Ember.debug('Ember.VERSION : ' + Ember.VERSION); + Ember.debug('Handlebars.VERSION : ' + Ember.Handlebars.VERSION); + Ember.debug('jQuery.VERSION : ' + Ember.$().jquery); + Ember.debug('-------------------------------'); + } + }, + + /** + @private + + Build the container for the current application. + + Also register a default application view in case the application + itself does not. + + @method buildContainer + @return {Ember.Container} the configured container + */ + buildContainer: function() { + var container = this.__container__ = Application.buildContainer(this); + + return container; + }, + + /** + @private + + If the application has not opted out of routing and has not explicitly + defined a router, supply a default router for the application author + to configure. + + This allows application developers to do: + + ```javascript + var App = Ember.Application.create(); + + App.Router.map(function() { + this.resource('posts'); + }); + ``` + + @method defaultRouter + @return {Ember.Router} the default router + */ + defaultRouter: function() { + // Create a default App.Router if one was not supplied to make + // it possible to do App.Router.map(...) without explicitly + // creating a router first. + if (this.router === undefined) { + return Ember.Router.extend(); + } + }, + + /** + @private + + Automatically initialize the application once the DOM has + become ready. + + The initialization itself is scheduled on the actions queue + which ensures that application loading finishes before + booting. + + If you are asynchronously loading code, you should call + `deferReadiness()` to defer booting, and then call + `advanceReadiness()` once all of your code has finished + loading. + + @method scheduleInitialize + */ + scheduleInitialize: function() { + var self = this; + + if (!this.$ || this.$.isReady) { + Ember.run.schedule('actions', self, '_initialize'); + } else { + this.$().ready(function(){ + Ember.run(self, '_initialize'); + }); + } + }, + + /** + Use this to defer readiness until some condition is true. + + Example: + + ```javascript + App = Ember.Application.create(); + App.deferReadiness(); + + jQuery.getJSON("/auth-token", function(token) { + App.token = token; + App.advanceReadiness(); + }); + ``` + + This allows you to perform asynchronous setup logic and defer + booting your application until the setup has finished. + + However, if the setup requires a loading UI, it might be better + to use the router for this purpose. + + @method deferReadiness + */ + deferReadiness: function() { + Ember.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0); + this._readinessDeferrals++; + }, + + /** + @method advanceReadiness + @see {Ember.Application#deferReadiness} + */ + advanceReadiness: function() { + this._readinessDeferrals--; + + if (this._readinessDeferrals === 0) { + Ember.run.once(this, this.didBecomeReady); + } + }, + + /** + registers a factory for later injection + + Example: + + ```javascript + App = Ember.Application.create(); + + App.Person = Ember.Object.extend({}); + App.Orange = Ember.Object.extend({}); + App.Email = Ember.Object.extend({}); + + App.register('model:user', App.Person, {singleton: false }); + App.register('fruit:favorite', App.Orange); + App.register('communication:main', App.Email, {singleton: false}); + ``` + + @method register + @param type {String} + @param name {String} + @param factory {String} + @param options {String} (optional) + **/ + register: function() { + var container = this.__container__; + container.register.apply(container, arguments); + }, + /** + defines an injection or typeInjection + + Example: + + ```javascript + App.inject(<full_name or type>, <property name>, <full_name>) + App.inject('model:user', 'email', 'model:email') + App.inject('model', 'source', 'source:main') + ``` + + @method inject + @param factoryNameOrType {String} + @param property {String} + @param injectionName {String} + **/ + inject: function(){ + var container = this.__container__; + container.injection.apply(container, arguments); + }, + + /** + @private + @deprecated + + Calling initialize manually is not supported. + + Please see Ember.Application#advanceReadiness and + Ember.Application#deferReadiness. + + @method initialize + **/ + initialize: function(){ + Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); + }, + /** + @private + + Initialize the application. This happens automatically. + + Run any initializers and run the application load hook. These hooks may + choose to defer readiness. For example, an authentication hook might want + to defer readiness until the auth token has been retrieved. + + @method _initialize + */ + _initialize: function() { + if (this.isDestroyed) { return; } + + // At this point, the App.Router must already be assigned + this.register('router:main', this.Router); + + this.runInitializers(); + Ember.runLoadHooks('application', this); + + // At this point, any initializers or load hooks that would have wanted + // to defer readiness have fired. In general, advancing readiness here + // will proceed to didBecomeReady. + this.advanceReadiness(); + + return this; + }, + + /** + Reset the application. This is typically used only in tests. It cleans up + the application in the following order: + + 1. Deactivate existing routes + 2. Destroy all objects in the container + 3. Create a new application container + 4. Re-route to the existing url + + Typical Example: + + ```javascript + + var App; + + Ember.run(function(){ + App = Ember.Application.create(); + }); + + module("acceptance test", { + setup: function() { + App.reset(); + } + }); + + test("first test", function(){ + // App is freshly reset + }); + + test("first test", function(){ + // App is again freshly reset + }); + ``` + + Advanced Example: + + Occasionally you may want to prevent the app from initializing during + setup. This could enable extra configuration, or enable asserting prior + to the app becoming ready. + + ```javascript + + var App; + + Ember.run(function(){ + App = Ember.Application.create(); + }); + + module("acceptance test", { + setup: function() { + Ember.run(function() { + App.reset(); + App.deferReadiness(); + }); + } + }); + + test("first test", function(){ + ok(true, 'something before app is initialized'); + + Ember.run(function(){ + App.advanceReadiness(); + }); + ok(true, 'something after app is initialized'); + }); + ``` + + @method reset + **/ + reset: function() { + this._readinessDeferrals = 1; + + function handleReset() { + var router = this.__container__.lookup('router:main'); + router.reset(); + + Ember.run(this.__container__, 'destroy'); + + this.buildContainer(); + + Ember.run.schedule('actions', this, function(){ + this._initialize(); + this.startRouting(); + }); + } + + Ember.run.join(this, handleReset); + }, + + /** + @private + @method runInitializers + */ + runInitializers: function() { + var initializers = get(this.constructor, 'initializers'), + container = this.__container__, + graph = new Ember.DAG(), + namespace = this, + i, initializer; + + for (i=0; i<initializers.length; i++) { + initializer = initializers[i]; + graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after); + } + + graph.topsort(function (vertex) { + var initializer = vertex.value; + Ember.assert("No application initializer named '"+vertex.name+"'", initializer); + initializer(container, namespace); + }); + }, + + /** + @private + @method didBecomeReady + */ + didBecomeReady: function() { + this.setupEventDispatcher(); + this.ready(); // user hook + this.startRouting(); + + if (!Ember.testing) { + // Eagerly name all classes that are already loaded + Ember.Namespace.processAll(); + Ember.BOOTED = true; + } + + this.resolve(this); + }, + + /** + @private + + Setup up the event dispatcher to receive events on the + application's `rootElement` with any registered + `customEvents`. + + @method setupEventDispatcher + */ + setupEventDispatcher: function() { + var customEvents = get(this, 'customEvents'), + rootElement = get(this, 'rootElement'), + dispatcher = this.__container__.lookup('event_dispatcher:main'); + + set(this, 'eventDispatcher', dispatcher); + dispatcher.setup(customEvents, rootElement); + }, + + /** + @private + + If the application has a router, use it to route to the current URL, and + trigger a new call to `route` whenever the URL changes. + + @method startRouting + @property router {Ember.Router} + */ + startRouting: function() { + var router = this.__container__.lookup('router:main'); + if (!router) { return; } + + router.startRouting(); + }, + + handleURL: function(url) { + var router = this.__container__.lookup('router:main'); + + router.handleURL(url); + }, + + /** + Called when the Application has become ready. + The call will be delayed until the DOM has become ready. + + @event ready + */ + ready: Ember.K, + + /** + Set this to provide an alternate class to `Ember.DefaultResolver` + + @property resolver + */ + resolver: null, + + willDestroy: function() { + Ember.BOOTED = false; + + this.__container__.destroy(); + }, + + initializer: function(options) { + this.constructor.initializer(options); + } +}); + +Ember.Application.reopenClass({ + concatenatedProperties: ['initializers'], + initializers: Ember.A(), + initializer: function(initializer) { + var initializers = get(this, 'initializers'); + + Ember.assert("The initializer '" + initializer.name + "' has already been registered", !initializers.findProperty('name', initializers.name)); + Ember.assert("An injection cannot be registered with both a before and an after", !(initializer.before && initializer.after)); + Ember.assert("An injection cannot be registered without an injection function", Ember.canInvoke(initializer, 'initialize')); + + initializers.push(initializer); + }, + + /** + @private + + This creates a container with the default Ember naming conventions. + + It also configures the container: + + * registered views are created every time they are looked up (they are + not singletons) + * registered templates are not factories; the registered value is + returned directly. + * the router receives the application as its `namespace` property + * all controllers receive the router as their `target` and `controllers` + properties + * all controllers receive the application as their `namespace` property + * the application view receives the application controller as its + `controller` property + * the application view receives the application template as its + `defaultTemplate` property + + @method buildContainer + @static + @param {Ember.Application} namespace the application to build the + container for. + @return {Ember.Container} the built container + */ + buildContainer: function(namespace) { + var container = new Ember.Container(); + + Ember.Container.defaultContainer = new DeprecatedContainer(container); + + container.set = Ember.set; + container.normalize = normalize; + container.resolver = resolverFor(namespace); + container.optionsForType('view', { singleton: false }); + container.optionsForType('template', { instantiate: false }); + container.register('application:main', namespace, { instantiate: false }); + + container.register('controller:basic', Ember.Controller, { instantiate: false }); + container.register('controller:object', Ember.ObjectController, { instantiate: false }); + container.register('controller:array', Ember.ArrayController, { instantiate: false }); + container.register('route:basic', Ember.Route, { instantiate: false }); + container.register('event_dispatcher:main', Ember.EventDispatcher); + + container.injection('router:main', 'namespace', 'application:main'); + + container.injection('controller', 'target', 'router:main'); + container.injection('controller', 'namespace', 'application:main'); + + container.injection('route', 'router', 'router:main'); + + return container; + } +}); + +/** + @private + + This function defines the default lookup rules for container lookups: + + * templates are looked up on `Ember.TEMPLATES` + * other names are looked up on the application after classifying the name. + For example, `controller:post` looks up `App.PostController` by default. + * if the default lookup fails, look for registered classes on the container + + This allows the application to register default injections in the container + that could be overridden by the normal naming convention. + + @method resolverFor + @param {Ember.Namespace} namespace the namespace to look for classes + @return {*} the resolved value for a given lookup +*/ +function resolverFor(namespace) { + var resolverClass = namespace.get('resolver') || Ember.DefaultResolver; + var resolver = resolverClass.create({ + namespace: namespace + }); + return function(fullName) { + return resolver.resolve(fullName); + }; +} + +function normalize(fullName) { + var split = fullName.split(':', 2), + type = split[0], + name = split[1]; + + Ember.assert("Tried to normalize a container name without a colon (:) in it. You probably tried to lookup a name that did not contain a type, a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); + + if (type !== 'template') { + var result = name; + + if (result.indexOf('.') > -1) { + result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + } + + if (name.indexOf('_') > -1) { + result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + } + + return type + ':' + result; + } else { + return fullName; + } +} + +Ember.runLoadHooks('Ember.Application', Ember.Application); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +@module ember +@submodule ember-application +*/ + +var get = Ember.get, set = Ember.set; +var ControllersProxy = Ember.Object.extend({ + controller: null, + + unknownProperty: function(controllerName) { + var controller = get(this, 'controller'), + needs = get(controller, 'needs'), + container = controller.get('container'), + dependency; + + for (var i=0, l=needs.length; i<l; i++) { + dependency = needs[i]; + if (dependency === controllerName) { + return container.lookup('controller:' + controllerName); + } + } + } +}); + +function verifyDependencies(controller) { + var needs = get(controller, 'needs'), + container = get(controller, 'container'), + dependency, satisfied = true; + + for (var i=0, l=needs.length; i<l; i++) { + dependency = needs[i]; + if (dependency.indexOf(':') === -1) { + dependency = "controller:" + dependency; + } + + if (!container.has(dependency)) { + satisfied = false; + Ember.assert(controller + " needs " + dependency + " but it does not exist", false); + } + } + + return satisfied; +} + +Ember.ControllerMixin.reopen({ + concatenatedProperties: ['needs'], + + /** + An array of other controller objects available inside + instances of this controller via the `controllers` + property: + + For example, when you define a controller: + + ```javascript + App.CommentsController = Ember.ArrayController.extend({ + needs: ['post'] + }); + ``` + + The application's single instance of these other + controllers are accessible by name through the + `controllers` property: + + ```javascript + this.get('controllers.post'); // instance of App.PostController + ``` + + This is only available for singleton controllers. + + @property {Array} needs + @default [] + */ + needs: [], + + init: function() { + this._super.apply(this, arguments); + + // Structure asserts to still do verification but not string concat in production + if(!verifyDependencies(this)) { + Ember.assert("Missing dependencies", false); + } + }, + + controllerFor: function(controllerName) { + Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); + var container = get(this, 'container'); + return container.lookup('controller:' + controllerName); + }, + + controllers: Ember.computed(function() { + return ControllersProxy.create({ controller: this }); + }) +}); + +})(); + + + +(function() { + +})(); + + + +(function() { +/** +Ember Application + +@module ember +@submodule ember-application +@requires ember-views, ember-states, ember-routing +*/ + +})(); + +(function() { +var get = Ember.get, set = Ember.set; + +/** +@module ember +@submodule ember-states +*/ + +/** + @class State + @namespace Ember + @extends Ember.Object + @uses Ember.Evented +*/ +Ember.State = Ember.Object.extend(Ember.Evented, +/** @scope Ember.State.prototype */{ + /** + A reference to the parent state. + + @property parentState + @type Ember.State + */ + parentState: null, + start: null, + + /** + The name of this state. + + @property name + @type String + */ + name: null, + + /** + The full path to this state. + + @property path + @type String + */ + path: Ember.computed(function() { + var parentPath = get(this, 'parentState.path'), + path = get(this, 'name'); + + if (parentPath) { + path = parentPath + '.' + path; + } + + return path; + }), + + /** + @private + + Override the default event firing from `Ember.Evented` to + also call methods with the given name. + + @method trigger + @param name + */ + trigger: function(name) { + if (this[name]) { + this[name].apply(this, [].slice.call(arguments, 1)); + } + this._super.apply(this, arguments); + }, + + init: function() { + var states = get(this, 'states'); + set(this, 'childStates', Ember.A()); + set(this, 'eventTransitions', get(this, 'eventTransitions') || {}); + + var name, value, transitionTarget; + + // As a convenience, loop over the properties + // of this state and look for any that are other + // Ember.State instances or classes, and move them + // to the `states` hash. This avoids having to + // create an explicit separate hash. + + if (!states) { + states = {}; + + for (name in this) { + if (name === "constructor") { continue; } + + if (value = this[name]) { + if (transitionTarget = value.transitionTarget) { + this.eventTransitions[name] = transitionTarget; + } + + this.setupChild(states, name, value); + } + } + + set(this, 'states', states); + } else { + for (name in states) { + this.setupChild(states, name, states[name]); + } + } + + // pathsCaches is a nested hash of the form: + // pathsCaches[stateManagerTypeGuid][path] == transitions_hash + set(this, 'pathsCaches', {}); + }, + + setPathsCache: function(stateManager, path, transitions) { + var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor), + pathsCaches = get(this, 'pathsCaches'), + pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {}; + + pathsCacheForManager[path] = transitions; + pathsCaches[stateManagerTypeGuid] = pathsCacheForManager; + }, + + getPathsCache: function(stateManager, path) { + var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor), + pathsCaches = get(this, 'pathsCaches'), + pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {}; + + return pathsCacheForManager[path]; + }, + + setupChild: function(states, name, value) { + if (!value) { return false; } + var instance; + + if (value instanceof Ember.State) { + set(value, 'name', name); + instance = value; + instance.container = this.container; + } else if (Ember.State.detect(value)) { + instance = value.create({ + name: name, + container: this.container + }); + } + + if (instance instanceof Ember.State) { + set(instance, 'parentState', this); + get(this, 'childStates').pushObject(instance); + states[name] = instance; + return instance; + } + }, + + lookupEventTransition: function(name) { + var path, state = this; + + while(state && !path) { + path = state.eventTransitions[name]; + state = state.get('parentState'); + } + + return path; + }, + + /** + A Boolean value indicating whether the state is a leaf state + in the state hierarchy. This is `false` if the state has child + states; otherwise it is true. + + @property isLeaf + @type Boolean + */ + isLeaf: Ember.computed(function() { + return !get(this, 'childStates').length; + }), + + /** + A boolean value indicating whether the state takes a context. + By default we assume all states take contexts. + + @property hasContext + @default true + */ + hasContext: true, + + /** + This is the default transition event. + + @event setup + @param {Ember.StateManager} manager + @param context + @see Ember.StateManager#transitionEvent + */ + setup: Ember.K, + + /** + This event fires when the state is entered. + + @event enter + @param {Ember.StateManager} manager + */ + enter: Ember.K, + + /** + This event fires when the state is exited. + + @event exit + @param {Ember.StateManager} manager + */ + exit: Ember.K +}); + +Ember.State.reopenClass({ + + /** + Creates an action function for transitioning to the named state while + preserving context. + + The following example StateManagers are equivalent: + + ```javascript + aManager = Ember.StateManager.create({ + stateOne: Ember.State.create({ + changeToStateTwo: Ember.State.transitionTo('stateTwo') + }), + stateTwo: Ember.State.create({}) + }) + + bManager = Ember.StateManager.create({ + stateOne: Ember.State.create({ + changeToStateTwo: function(manager, context){ + manager.transitionTo('stateTwo', context) + } + }), + stateTwo: Ember.State.create({}) + }) + ``` + + @method transitionTo + @static + @param {String} target + */ + + transitionTo: function(target) { + + var transitionFunction = function(stateManager, contextOrEvent) { + var contexts = [], + Event = Ember.$ && Ember.$.Event; + + if (contextOrEvent && (Event && contextOrEvent instanceof Event)) { + if (contextOrEvent.hasOwnProperty('contexts')) { + contexts = contextOrEvent.contexts.slice(); + } + } + else { + contexts = [].slice.call(arguments, 1); + } + + contexts.unshift(target); + stateManager.transitionTo.apply(stateManager, contexts); + }; + + transitionFunction.transitionTarget = target; + + return transitionFunction; + } + +}); + +})(); + + + +(function() { +/** +@module ember +@submodule ember-states +*/ + +var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; +var arrayForEach = Ember.ArrayPolyfills.forEach; +/** + A Transition takes the enter, exit and resolve states and normalizes + them: + + * takes any passed in contexts into consideration + * adds in `initialState`s + + @class Transition + @private +*/ +var Transition = function(raw) { + this.enterStates = raw.enterStates.slice(); + this.exitStates = raw.exitStates.slice(); + this.resolveState = raw.resolveState; + + this.finalState = raw.enterStates[raw.enterStates.length - 1] || raw.resolveState; +}; + +Transition.prototype = { + /** + Normalize the passed in enter, exit and resolve states. + + This process also adds `finalState` and `contexts` to the Transition object. + + @method normalize + @param {Ember.StateManager} manager the state manager running the transition + @param {Array} contexts a list of contexts passed into `transitionTo` + */ + normalize: function(manager, contexts) { + this.matchContextsToStates(contexts); + this.addInitialStates(); + this.removeUnchangedContexts(manager); + return this; + }, + + /** + Match each of the contexts passed to `transitionTo` to a state. + This process may also require adding additional enter and exit + states if there are more contexts than enter states. + + @method matchContextsToStates + @param {Array} contexts a list of contexts passed into `transitionTo` + */ + matchContextsToStates: function(contexts) { + var stateIdx = this.enterStates.length - 1, + matchedContexts = [], + state, + context; + + // Next, we will match the passed in contexts to the states they + // represent. + // + // First, assign a context to each enter state in reverse order. If + // any contexts are left, add a parent state to the list of states + // to enter and exit, and assign a context to the parent state. + // + // If there are still contexts left when the state manager is + // reached, raise an exception. + // + // This allows the following: + // + // |- root + // | |- post + // | | |- comments + // | |- about (* current state) + // + // For `transitionTo('post.comments', post, post.get('comments')`, + // the first context (`post`) will be assigned to `root.post`, and + // the second context (`post.get('comments')`) will be assigned + // to `root.post.comments`. + // + // For the following: + // + // |- root + // | |- post + // | | |- index (* current state) + // | | |- comments + // + // For `transitionTo('post.comments', otherPost, otherPost.get('comments')`, + // the `<root.post>` state will be added to the list of enter and exit + // states because its context has changed. + + while (contexts.length > 0) { + if (stateIdx >= 0) { + state = this.enterStates[stateIdx--]; + } else { + if (this.enterStates.length) { + state = get(this.enterStates[0], 'parentState'); + if (!state) { throw "Cannot match all contexts to states"; } + } else { + // If re-entering the current state with a context, the resolve + // state will be the current state. + state = this.resolveState; + } + + this.enterStates.unshift(state); + this.exitStates.unshift(state); + } + + // in routers, only states with dynamic segments have a context + if (get(state, 'hasContext')) { + context = contexts.pop(); + } else { + context = null; + } + + matchedContexts.unshift(context); + } + + this.contexts = matchedContexts; + }, + + /** + Add any `initialState`s to the list of enter states. + + @method addInitialStates + */ + addInitialStates: function() { + var finalState = this.finalState, initialState; + + while(true) { + initialState = get(finalState, 'initialState') || 'start'; + finalState = get(finalState, 'states.' + initialState); + + if (!finalState) { break; } + + this.finalState = finalState; + this.enterStates.push(finalState); + this.contexts.push(undefined); + } + }, + + /** + Remove any states that were added because the number of contexts + exceeded the number of explicit enter states, but the context has + not changed since the last time the state was entered. + + @method removeUnchangedContexts + @param {Ember.StateManager} manager passed in to look up the last + context for a states + */ + removeUnchangedContexts: function(manager) { + // Start from the beginning of the enter states. If the state was added + // to the list during the context matching phase, make sure the context + // has actually changed since the last time the state was entered. + while (this.enterStates.length > 0) { + if (this.enterStates[0] !== this.exitStates[0]) { break; } + + if (this.enterStates.length === this.contexts.length) { + if (manager.getStateMeta(this.enterStates[0], 'context') !== this.contexts[0]) { break; } + this.contexts.shift(); + } + + this.resolveState = this.enterStates.shift(); + this.exitStates.shift(); + } + } +}; + +var sendRecursively = function(event, currentState, isUnhandledPass) { + var log = this.enableLogging, + eventName = isUnhandledPass ? 'unhandledEvent' : event, + action = currentState[eventName], + contexts, sendRecursiveArguments, actionArguments; + + contexts = [].slice.call(arguments, 3); + + // Test to see if the action is a method that + // can be invoked. Don't blindly check just for + // existence, because it is possible the state + // manager has a child state of the given name, + // and we should still raise an exception in that + // case. + if (typeof action === 'function') { + if (log) { + if (isUnhandledPass) { + Ember.Logger.log(fmt("STATEMANAGER: Unhandled event '%@' being sent to state %@.", [event, get(currentState, 'path')])); + } else { + Ember.Logger.log(fmt("STATEMANAGER: Sending event '%@' to state %@.", [event, get(currentState, 'path')])); + } + } + + actionArguments = contexts; + if (isUnhandledPass) { + actionArguments.unshift(event); + } + actionArguments.unshift(this); + + return action.apply(currentState, actionArguments); + } else { + var parentState = get(currentState, 'parentState'); + if (parentState) { + + sendRecursiveArguments = contexts; + sendRecursiveArguments.unshift(event, parentState, isUnhandledPass); + + return sendRecursively.apply(this, sendRecursiveArguments); + } else if (!isUnhandledPass) { + return sendEvent.call(this, event, contexts, true); + } + } +}; + +var sendEvent = function(eventName, sendRecursiveArguments, isUnhandledPass) { + sendRecursiveArguments.unshift(eventName, get(this, 'currentState'), isUnhandledPass); + return sendRecursively.apply(this, sendRecursiveArguments); +}; + +/** + StateManager is part of Ember's implementation of a finite state machine. A + StateManager instance manages a number of properties that are instances of + `Ember.State`, + tracks the current active state, and triggers callbacks when states have changed. + + ## Defining States + + The states of StateManager can be declared in one of two ways. First, you can + define a `states` property that contains all the states: + + ```javascript + managerA = Ember.StateManager.create({ + states: { + stateOne: Ember.State.create(), + stateTwo: Ember.State.create() + } + }) + + managerA.get('states') + // { + // stateOne: Ember.State.create(), + // stateTwo: Ember.State.create() + // } + ``` + + You can also add instances of `Ember.State` (or an `Ember.State` subclass) + directly as properties of a StateManager. These states will be collected into + the `states` property for you. + + ```javascript + managerA = Ember.StateManager.create({ + stateOne: Ember.State.create(), + stateTwo: Ember.State.create() + }) + + managerA.get('states') + // { + // stateOne: Ember.State.create(), + // stateTwo: Ember.State.create() + // } + ``` + + ## The Initial State + + When created a StateManager instance will immediately enter into the state + defined as its `start` property or the state referenced by name in its + `initialState` property: + + ```javascript + managerA = Ember.StateManager.create({ + start: Ember.State.create({}) + }) + + managerA.get('currentState.name') // 'start' + + managerB = Ember.StateManager.create({ + initialState: 'beginHere', + beginHere: Ember.State.create({}) + }) + + managerB.get('currentState.name') // 'beginHere' + ``` + + Because it is a property you may also provide a computed function if you wish + to derive an `initialState` programmatically: + + ```javascript + managerC = Ember.StateManager.create({ + initialState: function(){ + if (someLogic) { + return 'active'; + } else { + return 'passive'; + } + }.property(), + active: Ember.State.create({}), + passive: Ember.State.create({}) + }) + ``` + + ## Moving Between States + + A StateManager can have any number of `Ember.State` objects as properties + and can have a single one of these states as its current state. + + Calling `transitionTo` transitions between states: + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown', + poweredDown: Ember.State.create({}), + poweredUp: Ember.State.create({}) + }) + + robotManager.get('currentState.name') // 'poweredDown' + robotManager.transitionTo('poweredUp') + robotManager.get('currentState.name') // 'poweredUp' + ``` + + Before transitioning into a new state the existing `currentState` will have + its `exit` method called with the StateManager instance as its first argument + and an object representing the transition as its second argument. + + After transitioning into a new state the new `currentState` will have its + `enter` method called with the StateManager instance as its first argument + and an object representing the transition as its second argument. + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown', + poweredDown: Ember.State.create({ + exit: function(stateManager){ + console.log("exiting the poweredDown state") + } + }), + poweredUp: Ember.State.create({ + enter: function(stateManager){ + console.log("entering the poweredUp state. Destroy all humans.") + } + }) + }) + + robotManager.get('currentState.name') // 'poweredDown' + robotManager.transitionTo('poweredUp') + + // will log + // 'exiting the poweredDown state' + // 'entering the poweredUp state. Destroy all humans.' + ``` + + Once a StateManager is already in a state, subsequent attempts to enter that + state will not trigger enter or exit method calls. Attempts to transition + into a state that the manager does not have will result in no changes in the + StateManager's current state: + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown', + poweredDown: Ember.State.create({ + exit: function(stateManager){ + console.log("exiting the poweredDown state") + } + }), + poweredUp: Ember.State.create({ + enter: function(stateManager){ + console.log("entering the poweredUp state. Destroy all humans.") + } + }) + }) + + robotManager.get('currentState.name') // 'poweredDown' + robotManager.transitionTo('poweredUp') + // will log + // 'exiting the poweredDown state' + // 'entering the poweredUp state. Destroy all humans.' + robotManager.transitionTo('poweredUp') // no logging, no state change + + robotManager.transitionTo('someUnknownState') // silently fails + robotManager.get('currentState.name') // 'poweredUp' + ``` + + Each state property may itself contain properties that are instances of + `Ember.State`. The StateManager can transition to specific sub-states in a + series of transitionTo method calls or via a single transitionTo with the + full path to the specific state. The StateManager will also keep track of the + full path to its currentState + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown', + poweredDown: Ember.State.create({ + charging: Ember.State.create(), + charged: Ember.State.create() + }), + poweredUp: Ember.State.create({ + mobile: Ember.State.create(), + stationary: Ember.State.create() + }) + }) + + robotManager.get('currentState.name') // 'poweredDown' + + robotManager.transitionTo('poweredUp') + robotManager.get('currentState.name') // 'poweredUp' + + robotManager.transitionTo('mobile') + robotManager.get('currentState.name') // 'mobile' + + // transition via a state path + robotManager.transitionTo('poweredDown.charging') + robotManager.get('currentState.name') // 'charging' + + robotManager.get('currentState.path') // 'poweredDown.charging' + ``` + + Enter transition methods will be called for each state and nested child state + in their hierarchical order. Exit methods will be called for each state and + its nested states in reverse hierarchical order. + + Exit transitions for a parent state are not called when entering into one of + its child states, only when transitioning to a new section of possible states + in the hierarchy. + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown', + poweredDown: Ember.State.create({ + enter: function(){}, + exit: function(){ + console.log("exited poweredDown state") + }, + charging: Ember.State.create({ + enter: function(){}, + exit: function(){} + }), + charged: Ember.State.create({ + enter: function(){ + console.log("entered charged state") + }, + exit: function(){ + console.log("exited charged state") + } + }) + }), + poweredUp: Ember.State.create({ + enter: function(){ + console.log("entered poweredUp state") + }, + exit: function(){}, + mobile: Ember.State.create({ + enter: function(){ + console.log("entered mobile state") + }, + exit: function(){} + }), + stationary: Ember.State.create({ + enter: function(){}, + exit: function(){} + }) + }) + }) + + + robotManager.get('currentState.path') // 'poweredDown' + robotManager.transitionTo('charged') + // logs 'entered charged state' + // but does *not* log 'exited poweredDown state' + robotManager.get('currentState.name') // 'charged + + robotManager.transitionTo('poweredUp.mobile') + // logs + // 'exited charged state' + // 'exited poweredDown state' + // 'entered poweredUp state' + // 'entered mobile state' + ``` + + During development you can set a StateManager's `enableLogging` property to + `true` to receive console messages of state transitions. + + ```javascript + robotManager = Ember.StateManager.create({ + enableLogging: true + }) + ``` + + ## Managing currentState with Actions + + To control which transitions are possible for a given state, and + appropriately handle external events, the StateManager can receive and + route action messages to its states via the `send` method. Calling to + `send` with an action name will begin searching for a method with the same + name starting at the current state and moving up through the parent states + in a state hierarchy until an appropriate method is found or the StateManager + instance itself is reached. + + If an appropriately named method is found it will be called with the state + manager as the first argument and an optional `context` object as the second + argument. + + ```javascript + managerA = Ember.StateManager.create({ + initialState: 'stateOne.substateOne.subsubstateOne', + stateOne: Ember.State.create({ + substateOne: Ember.State.create({ + anAction: function(manager, context){ + console.log("an action was called") + }, + subsubstateOne: Ember.State.create({}) + }) + }) + }) + + managerA.get('currentState.name') // 'subsubstateOne' + managerA.send('anAction') + // 'stateOne.substateOne.subsubstateOne' has no anAction method + // so the 'anAction' method of 'stateOne.substateOne' is called + // and logs "an action was called" + // with managerA as the first argument + // and no second argument + + someObject = {} + managerA.send('anAction', someObject) + // the 'anAction' method of 'stateOne.substateOne' is called again + // with managerA as the first argument and + // someObject as the second argument. + ``` + + If the StateManager attempts to send an action but does not find an appropriately named + method in the current state or while moving upwards through the state hierarchy, it will + repeat the process looking for a `unhandledEvent` method. If an `unhandledEvent` method is + found, it will be called with the original event name as the second argument. If an + `unhandledEvent` method is not found, the StateManager will throw a new Ember.Error. + + ```javascript + managerB = Ember.StateManager.create({ + initialState: 'stateOne.substateOne.subsubstateOne', + stateOne: Ember.State.create({ + substateOne: Ember.State.create({ + subsubstateOne: Ember.State.create({}), + unhandledEvent: function(manager, eventName, context) { + console.log("got an unhandledEvent with name " + eventName); + } + }) + }) + }) + + managerB.get('currentState.name') // 'subsubstateOne' + managerB.send('anAction') + // neither `stateOne.substateOne.subsubstateOne` nor any of it's + // parent states have a handler for `anAction`. `subsubstateOne` + // also does not have a `unhandledEvent` method, but its parent + // state, `substateOne`, does, and it gets fired. It will log + // "got an unhandledEvent with name anAction" + ``` + + Action detection only moves upwards through the state hierarchy from the current state. + It does not search in other portions of the hierarchy. + + ```javascript + managerC = Ember.StateManager.create({ + initialState: 'stateOne.substateOne.subsubstateOne', + stateOne: Ember.State.create({ + substateOne: Ember.State.create({ + subsubstateOne: Ember.State.create({}) + }) + }), + stateTwo: Ember.State.create({ + anAction: function(manager, context){ + // will not be called below because it is + // not a parent of the current state + } + }) + }) + + managerC.get('currentState.name') // 'subsubstateOne' + managerC.send('anAction') + // Error: <Ember.StateManager:ember132> could not + // respond to event anAction in state stateOne.substateOne.subsubstateOne. + ``` + + Inside of an action method the given state should delegate `transitionTo` calls on its + StateManager. + + ```javascript + robotManager = Ember.StateManager.create({ + initialState: 'poweredDown.charging', + poweredDown: Ember.State.create({ + charging: Ember.State.create({ + chargeComplete: function(manager, context){ + manager.transitionTo('charged') + } + }), + charged: Ember.State.create({ + boot: function(manager, context){ + manager.transitionTo('poweredUp') + } + }) + }), + poweredUp: Ember.State.create({ + beginExtermination: function(manager, context){ + manager.transitionTo('rampaging') + }, + rampaging: Ember.State.create() + }) + }) + + robotManager.get('currentState.name') // 'charging' + robotManager.send('boot') // throws error, no boot action + // in current hierarchy + robotManager.get('currentState.name') // remains 'charging' + + robotManager.send('beginExtermination') // throws error, no beginExtermination + // action in current hierarchy + robotManager.get('currentState.name') // remains 'charging' + + robotManager.send('chargeComplete') + robotManager.get('currentState.name') // 'charged' + + robotManager.send('boot') + robotManager.get('currentState.name') // 'poweredUp' + + robotManager.send('beginExtermination', allHumans) + robotManager.get('currentState.name') // 'rampaging' + ``` + + Transition actions can also be created using the `transitionTo` method of the `Ember.State` class. The + following example StateManagers are equivalent: + + ```javascript + aManager = Ember.StateManager.create({ + stateOne: Ember.State.create({ + changeToStateTwo: Ember.State.transitionTo('stateTwo') + }), + stateTwo: Ember.State.create({}) + }) + + bManager = Ember.StateManager.create({ + stateOne: Ember.State.create({ + changeToStateTwo: function(manager, context){ + manager.transitionTo('stateTwo', context) + } + }), + stateTwo: Ember.State.create({}) + }) + ``` + + @class StateManager + @namespace Ember + @extends Ember.State +**/ +Ember.StateManager = Ember.State.extend({ + /** + @private + + When creating a new statemanager, look for a default state to transition + into. This state can either be named `start`, or can be specified using the + `initialState` property. + + @method init + */ + init: function() { + this._super(); + + set(this, 'stateMeta', Ember.Map.create()); + + var initialState = get(this, 'initialState'); + + if (!initialState && get(this, 'states.start')) { + initialState = 'start'; + } + + if (initialState) { + this.transitionTo(initialState); + Ember.assert('Failed to transition to initial state "' + initialState + '"', !!get(this, 'currentState')); + } + }, + + stateMetaFor: function(state) { + var meta = get(this, 'stateMeta'), + stateMeta = meta.get(state); + + if (!stateMeta) { + stateMeta = {}; + meta.set(state, stateMeta); + } + + return stateMeta; + }, + + setStateMeta: function(state, key, value) { + return set(this.stateMetaFor(state), key, value); + }, + + getStateMeta: function(state, key) { + return get(this.stateMetaFor(state), key); + }, + + /** + The current state from among the manager's possible states. This property should + not be set directly. Use `transitionTo` to move between states by name. + + @property currentState + @type Ember.State + */ + currentState: null, + + /** + The path of the current state. Returns a string representation of the current + state. + + @property currentPath + @type String + */ + currentPath: Ember.computed.alias('currentState.path'), + + /** + The name of transitionEvent that this stateManager will dispatch + + @property transitionEvent + @type String + @default 'setup' + */ + transitionEvent: 'setup', + + /** + If set to true, `errorOnUnhandledEvents` will cause an exception to be + raised if you attempt to send an event to a state manager that is not + handled by the current state or any of its parent states. + + @property errorOnUnhandledEvents + @type Boolean + @default true + */ + errorOnUnhandledEvent: true, + + send: function(event) { + var contexts = [].slice.call(arguments, 1); + Ember.assert('Cannot send event "' + event + '" while currentState is ' + get(this, 'currentState'), get(this, 'currentState')); + return sendEvent.call(this, event, contexts, false); + }, + unhandledEvent: function(manager, event) { + if (get(this, 'errorOnUnhandledEvent')) { + throw new Ember.Error(this.toString() + " could not respond to event " + event + " in state " + get(this, 'currentState.path') + "."); + } + }, + + /** + Finds a state by its state path. + + Example: + + ```javascript + manager = Ember.StateManager.create({ + root: Ember.State.create({ + dashboard: Ember.State.create() + }) + }); + + manager.getStateByPath(manager, "root.dashboard") + + // returns the dashboard state + ``` + + @method getStateByPath + @param {Ember.State} root the state to start searching from + @param {String} path the state path to follow + @return {Ember.State} the state at the end of the path + */ + getStateByPath: function(root, path) { + var parts = path.split('.'), + state = root; + + for (var i=0, len=parts.length; i<len; i++) { + state = get(get(state, 'states'), parts[i]); + if (!state) { break; } + } + + return state; + }, + + findStateByPath: function(state, path) { + var possible; + + while (!possible && state) { + possible = this.getStateByPath(state, path); + state = get(state, 'parentState'); + } + + return possible; + }, + + /** + A state stores its child states in its `states` hash. + This code takes a path like `posts.show` and looks + up `root.states.posts.states.show`. + + It returns a list of all of the states from the + root, which is the list of states to call `enter` + on. + + @method getStatesInPath + @param root + @param path + */ + getStatesInPath: function(root, path) { + if (!path || path === "") { return undefined; } + var parts = path.split('.'), + result = [], + states, + state; + + for (var i=0, len=parts.length; i<len; i++) { + states = get(root, 'states'); + if (!states) { return undefined; } + state = get(states, parts[i]); + if (state) { root = state; result.push(state); } + else { return undefined; } + } + + return result; + }, + + goToState: function() { + // not deprecating this yet so people don't constantly need to + // make trivial changes for little reason. + return this.transitionTo.apply(this, arguments); + }, + + transitionTo: function(path, context) { + // XXX When is transitionTo called with no path + if (Ember.isEmpty(path)) { return; } + + // The ES6 signature of this function is `path, ...contexts` + var contexts = context ? Array.prototype.slice.call(arguments, 1) : [], + currentState = get(this, 'currentState') || this; + + // First, get the enter, exit and resolve states for the current state + // and specified path. If possible, use an existing cache. + var hash = this.contextFreeTransition(currentState, path); + + // Next, process the raw state information for the contexts passed in. + var transition = new Transition(hash).normalize(this, contexts); + + this.enterState(transition); + this.triggerSetupContext(transition); + }, + + contextFreeTransition: function(currentState, path) { + var cache = currentState.getPathsCache(this, path); + if (cache) { return cache; } + + var enterStates = this.getStatesInPath(currentState, path), + exitStates = [], + resolveState = currentState; + + // Walk up the states. For each state, check whether a state matching + // the `path` is nested underneath. This will find the closest + // parent state containing `path`. + // + // This allows the user to pass in a relative path. For example, for + // the following state hierarchy: + // + // | |root + // | |- posts + // | | |- show (* current) + // | |- comments + // | | |- show + // + // If the current state is `<root.posts.show>`, an attempt to + // transition to `comments.show` will match `<root.comments.show>`. + // + // First, this code will look for root.posts.show.comments.show. + // Next, it will look for root.posts.comments.show. Finally, + // it will look for `root.comments.show`, and find the state. + // + // After this process, the following variables will exist: + // + // * resolveState: a common parent state between the current + // and target state. In the above example, `<root>` is the + // `resolveState`. + // * enterStates: a list of all of the states represented + // by the path from the `resolveState`. For example, for + // the path `root.comments.show`, `enterStates` would have + // `[<root.comments>, <root.comments.show>]` + // * exitStates: a list of all of the states from the + // `resolveState` to the `currentState`. In the above + // example, `exitStates` would have + // `[<root.posts>`, `<root.posts.show>]`. + while (resolveState && !enterStates) { + exitStates.unshift(resolveState); + + resolveState = get(resolveState, 'parentState'); + if (!resolveState) { + enterStates = this.getStatesInPath(this, path); + if (!enterStates) { + Ember.assert('Could not find state for path: "'+path+'"'); + return; + } + } + enterStates = this.getStatesInPath(resolveState, path); + } + + // If the path contains some states that are parents of both the + // current state and the target state, remove them. + // + // For example, in the following hierarchy: + // + // |- root + // | |- post + // | | |- index (* current) + // | | |- show + // + // If the `path` is `root.post.show`, the three variables will + // be: + // + // * resolveState: `<state manager>` + // * enterStates: `[<root>, <root.post>, <root.post.show>]` + // * exitStates: `[<root>, <root.post>, <root.post.index>]` + // + // The goal of this code is to remove the common states, so we + // have: + // + // * resolveState: `<root.post>` + // * enterStates: `[<root.post.show>]` + // * exitStates: `[<root.post.index>]` + // + // This avoid unnecessary calls to the enter and exit transitions. + while (enterStates.length > 0 && enterStates[0] === exitStates[0]) { + resolveState = enterStates.shift(); + exitStates.shift(); + } + + // Cache the enterStates, exitStates, and resolveState for the + // current state and the `path`. + var transitions = { + exitStates: exitStates, + enterStates: enterStates, + resolveState: resolveState + }; + + currentState.setPathsCache(this, path, transitions); + + return transitions; + }, + + triggerSetupContext: function(transitions) { + var contexts = transitions.contexts, + offset = transitions.enterStates.length - contexts.length, + enterStates = transitions.enterStates, + transitionEvent = get(this, 'transitionEvent'); + + Ember.assert("More contexts provided than states", offset >= 0); + + arrayForEach.call(enterStates, function(state, idx) { + state.trigger(transitionEvent, this, contexts[idx-offset]); + }, this); + }, + + getState: function(name) { + var state = get(this, name), + parentState = get(this, 'parentState'); + + if (state) { + return state; + } else if (parentState) { + return parentState.getState(name); + } + }, + + enterState: function(transition) { + var log = this.enableLogging; + + var exitStates = transition.exitStates.slice(0).reverse(); + arrayForEach.call(exitStates, function(state) { + state.trigger('exit', this); + }, this); + + arrayForEach.call(transition.enterStates, function(state) { + if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(state, 'path')); } + state.trigger('enter', this); + }, this); + + set(this, 'currentState', transition.finalState); + } +}); + +})(); + + + +(function() { +/** +Ember States + +@module ember +@submodule ember-states +@requires ember-runtime +*/ + +})(); + +(function() { +var slice = [].slice, + helpers = {}, + originalMethods = {}, + injectHelpersCallbacks = []; + +/** + @class Test + @namespace Ember +*/ +Ember.Test = { + + /** + @public + + `registerHelper` is used to register a + test helper that will be injected when + `App.injectTestHelpers` is called. + + The helper method will always be called + with the current Application as the first + parameter. + + For example: + ```javascript + Ember.Test.registerHelper('boot', function(app)) { + Ember.run(app, app.deferReadiness); + } + ``` + + This helper can later be called without arguments + because it will be called with `app` as the + first parameter. + + ```javascript + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); + ``` + + Whenever you register a helper that + performs async operations, + make sure you `return wait();` at the + end of the helper. + + If an async helper also needs to return a value, + pass it to the `wait` helper as a first argument: + `return wait(val);` + + @method registerHelper + @param name {String} + @param helperMethod {Function} + */ + registerHelper: function(name, helperMethod) { + helpers[name] = helperMethod; + }, + /** + @public + @method unregisterHelper + @param name {String} + */ + unregisterHelper: function(name) { + delete helpers[name]; + if (originalMethods[name]) { + window[name] = originalMethods[name]; + } + delete originalMethods[name]; + }, + + /** + @public + + Used to register callbacks to be fired + whenever `App.injectTestHelpers` is called + + The callback will receive the current application + as an argument. + + @method unregisterHelper + @param name {String} + */ + onInjectHelpers: function(callback) { + injectHelpersCallbacks.push(callback); + }, + + /** + @public + + This returns a thenable tailored + for testing. It catches failed + `onSuccess` callbacks and invokes + the `Ember.Test.adapter.exception` + callback in the last chained then. + + This method should be returned + by async helpers such as `wait`. + + @method promise + @param resolver {Function} + */ + promise: function(resolver) { + var promise = new Ember.RSVP.Promise(resolver); + var thenable = { + chained: false + }; + thenable.then = function(onSuccess, onFailure) { + var thenPromise, nextPromise; + thenable.chained = true; + thenPromise = promise.then(onSuccess, onFailure); + // this is to ensure all downstream fulfillment + // handlers are wrapped in the error handling + nextPromise = Ember.Test.promise(function(resolve) { + resolve(thenPromise); + }); + thenPromise.then(null, function(reason) { + // ensure this is the last promise in the chain + // if not, ignore and the exception will propagate + // this prevents the same error from being fired multiple times + if (!nextPromise.chained) { + Ember.Test.adapter.exception(reason); + } + }); + return nextPromise; + }; + return thenable; + }, + + /** + @public + + Used to allow ember-testing + to communicate with a specific + testing framework. + + You can manually set it before calling + `App.setupForTesting()`. + + Example: + 'Ember.Test.adapter = MyCustomAdapter.create()' + + If you do not set it, ember-testing + will default to `Ember.Test.QUnitAdapter`. + */ + adapter: null +}; + +function curry(app, fn) { + return function() { + var args = slice.call(arguments); + args.unshift(app); + return fn.apply(app, args); + }; +} + +Ember.Application.reopen({ + testHelpers: {}, + + setupForTesting: function() { + this.deferReadiness(); + + this.Router.reopen({ + location: 'none' + }); + + // if adapter is not manually set + // default to QUnit + if (!Ember.Test.adapter) { + Ember.Test.adapter = Ember.Test.QUnitAdapter.create(); + } + }, + + injectTestHelpers: function() { + this.testHelpers = {}; + for (var name in helpers) { + originalMethods[name] = window[name]; + this.testHelpers[name] = window[name] = curry(this, helpers[name]); + } + + for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { + injectHelpersCallbacks[i](this); + } + }, + + removeTestHelpers: function() { + for (var name in helpers) { + window[name] = originalMethods[name]; + delete this.testHelpers[name]; + delete originalMethods[name]; + } + } +}); + +})(); + + + +(function() { +var Test = Ember.Test; + +/** + @class Adapter + @namespace Ember.Test +*/ +Test.Adapter = Ember.Object.extend({ + /** + @public + + This callback will be called + whenever an async operation + is about to start. + + Override this to call your + framework's methods + that handle async operations + + @method asyncStart + */ + asyncStart: Ember.K, + + /** + @public + + This callback will be called + whenever an async operation + has completed. + + @method asyncEnd + */ + asyncEnd: Ember.K, + + /** + @public + + Override this method with your + testing framework's false assertion + This function is called whenever + an exception occurs causing the testing + promise to fail. + + QUnit example: + + ```javascript + exception: function(error) { + ok(false, error); + } + ``` + + @method exception + @param reason {String} + */ + exception: function(error) { + setTimeout(function() { + throw error; + }); + } +}); + +/** + @class QUnitAdapter + @namespace Ember.Test +*/ +Test.QUnitAdapter = Test.Adapter.extend({ + asyncStart: function() { + stop(); + }, + asyncEnd: function() { + start(); + }, + exception: function(error) { + ok(false, error); + } +}); + +})(); + + + +(function() { +var get = Ember.get, + Test = Ember.Test, + helper = Test.registerHelper, + countAsync = 0; + +Test.pendingAjaxRequests = 0; + +Test.onInjectHelpers(function() { + Ember.$(document).ajaxStart(function() { + Test.pendingAjaxRequests++; + }); + + Ember.$(document).ajaxStop(function() { + Test.pendingAjaxRequests--; + }); +}); + + +function visit(app, url) { + app.__container__.lookup('router:main').location.setURL(url); + Ember.run(app, app.handleURL, url); + return wait(app); +} + +function click(app, selector, context) { + var $el = findWithAssert(app, selector, context); + Ember.run($el, 'click'); + return wait(app); +} + +function fillIn(app, selector, context, text) { + var $el; + if (typeof text === 'undefined') { + text = context; + context = null; + } + $el = findWithAssert(app, selector, context); + Ember.run(function() { + $el.val(text).change(); + }); + return wait(app); +} + +function findWithAssert(app, selector, context) { + var $el = find(app, selector, context); + if ($el.length === 0) { + throw("Element " + selector + " not found."); + } + return $el; +} + +function find(app, selector, context) { + var $el; + context = context || get(app, 'rootElement'); + $el = app.$(selector, context); + + return $el; +} + +function wait(app, value) { + var promise; + + promise = Test.promise(function(resolve) { + if (++countAsync === 1) { + Test.adapter.asyncStart(); + } + var watcher = setInterval(function() { + var routerIsLoading = app.__container__.lookup('router:main').router.isLoading; + if (routerIsLoading) { return; } + if (Test.pendingAjaxRequests) { return; } + if (Ember.run.hasScheduledTimers() || Ember.run.currentRunLoop) { return; } + + clearInterval(watcher); + + if (--countAsync === 0) { + Test.adapter.asyncEnd(); + } + + Ember.run(null, resolve, value); + }, 10); + }); + + return buildChainObject(app, promise); +} + +/** + Builds an object that contains + all helper methods. This object will be + returned by helpers and then-promises. + + This allows us to chain helpers: + + ```javascript + visit('posts/new') + .click('.add-btn') + .fillIn('.title', 'Post') + .click('.submit') + .then(function() { + equal('.post-title', 'Post'); + }) + .visit('comments') + .then(function() { + equal(find('.comments'),length, 0); + }); + ``` +*/ +function buildChainObject(app, promise) { + var helperName, obj = {}; + for(helperName in app.testHelpers) { + obj[helperName] = chain(app, promise, app.testHelpers[helperName]); + } + obj.then = function(fn) { + var thenPromise = promise.then(fn); + return buildChainObject(app, thenPromise); + }; + return obj; +} + +function chain(app, promise, fn) { + return function() { + var args = arguments, chainedPromise; + chainedPromise = promise.then(function() { + return fn.apply(null, args); + }); + return buildChainObject(app, chainedPromise); + }; +} + +// expose these methods as test helpers +helper('visit', visit); +helper('click', click); +helper('fillIn', fillIn); +helper('find', find); +helper('findWithAssert', findWithAssert); +helper('wait', wait); + +})(); + + + +(function() { + +})(); + +(function() { +/** +Ember + +@module ember +*/ + +})(); + + +})(); diff --git a/js/libs/handlebars-1.0.0-rc.4.js b/js/libs/handlebars-1.0.0-rc.4.js new file mode 100755 index 0000000..96d86ea --- /dev/null +++ b/js/libs/handlebars-1.0.0-rc.4.js @@ -0,0 +1,2239 @@ +/* + +Copyright (C) 2011 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// lib/handlebars/browser-prefix.js +var Handlebars = {}; + +(function(Handlebars, undefined) { +; +// lib/handlebars/base.js + +Handlebars.VERSION = "1.0.0-rc.4"; +Handlebars.COMPILER_REVISION = 3; + +Handlebars.REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '>= 1.0.0-rc.4' +}; + +Handlebars.helpers = {}; +Handlebars.partials = {}; + +var toString = Object.prototype.toString, + functionType = '[object Function]', + objectType = '[object Object]'; + +Handlebars.registerHelper = function(name, fn, inverse) { + if (toString.call(name) === objectType) { + if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } + Handlebars.Utils.extend(this.helpers, name); + } else { + if (inverse) { fn.not = inverse; } + this.helpers[name] = fn; + } +}; + +Handlebars.registerPartial = function(name, str) { + if (toString.call(name) === objectType) { + Handlebars.Utils.extend(this.partials, name); + } else { + this.partials[name] = str; + } +}; + +Handlebars.registerHelper('helperMissing', function(arg) { + if(arguments.length === 2) { + return undefined; + } else { + throw new Error("Could not find property '" + arg + "'"); + } +}); + +Handlebars.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse || function() {}, fn = options.fn; + + var type = toString.call(context); + + if(type === functionType) { context = context.call(this); } + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if(type === "[object Array]") { + if(context.length > 0) { + return Handlebars.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + return fn(context); + } +}); + +Handlebars.K = function() {}; + +Handlebars.createFrame = Object.create || function(object) { + Handlebars.K.prototype = object; + var obj = new Handlebars.K(); + Handlebars.K.prototype = null; + return obj; +}; + +Handlebars.logger = { + DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, + + methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, + + // can be overridden in the host environment + log: function(level, obj) { + if (Handlebars.logger.level <= level) { + var method = Handlebars.logger.methodMap[level]; + if (typeof console !== 'undefined' && console[method]) { + console[method].call(console, obj); + } + } + } +}; + +Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; + +Handlebars.registerHelper('each', function(context, options) { + var fn = options.fn, inverse = options.inverse; + var i = 0, ret = "", data; + + if (options.data) { + data = Handlebars.createFrame(options.data); + } + + if(context && typeof context === 'object') { + if(context instanceof Array){ + for(var j = context.length; i<j; i++) { + if (data) { data.index = i; } + ret = ret + fn(context[i], { data: data }); + } + } else { + for(var key in context) { + if(context.hasOwnProperty(key)) { + if(data) { data.key = key; } + ret = ret + fn(context[key], {data: data}); + i++; + } + } + } + } + + if(i === 0){ + ret = inverse(this); + } + + return ret; +}); + +Handlebars.registerHelper('if', function(context, options) { + var type = toString.call(context); + if(type === functionType) { context = context.call(this); } + + if(!context || Handlebars.Utils.isEmpty(context)) { + return options.inverse(this); + } else { + return options.fn(this); + } +}); + +Handlebars.registerHelper('unless', function(context, options) { + return Handlebars.helpers['if'].call(this, context, {fn: options.inverse, inverse: options.fn}); +}); + +Handlebars.registerHelper('with', function(context, options) { + if (!Handlebars.Utils.isEmpty(context)) return options.fn(context); +}); + +Handlebars.registerHelper('log', function(context, options) { + var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; + Handlebars.log(level, context); +}); +; +// lib/handlebars/compiler/parser.js +/* Jison generated parser */ +var handlebars = (function(){ +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"root":3,"program":4,"EOF":5,"simpleInverse":6,"statements":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"partialName":25,"params":26,"hash":27,"DATA":28,"param":29,"STRING":30,"INTEGER":31,"BOOLEAN":32,"hashSegments":33,"hashSegment":34,"ID":35,"EQUALS":36,"PARTIAL_NAME":37,"pathSegments":38,"SEP":39,"$accept":0,"$end":1}, +terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"DATA",30:"STRING",31:"INTEGER",32:"BOOLEAN",35:"ID",36:"EQUALS",37:"PARTIAL_NAME",39:"SEP"}, +productions_: [0,[3,2],[4,2],[4,3],[4,2],[4,1],[4,1],[4,0],[7,1],[7,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[6,2],[17,3],[17,2],[17,2],[17,1],[17,1],[26,2],[26,1],[29,1],[29,1],[29,1],[29,1],[29,1],[27,1],[33,2],[33,1],[34,3],[34,3],[34,3],[34,3],[34,3],[25,1],[21,1],[38,3],[38,1]], +performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + +var $0 = $$.length - 1; +switch (yystate) { +case 1: return $$[$0-1]; +break; +case 2: this.$ = new yy.ProgramNode([], $$[$0]); +break; +case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]); +break; +case 4: this.$ = new yy.ProgramNode($$[$0-1], []); +break; +case 5: this.$ = new yy.ProgramNode($$[$0]); +break; +case 6: this.$ = new yy.ProgramNode([], []); +break; +case 7: this.$ = new yy.ProgramNode([]); +break; +case 8: this.$ = [$$[$0]]; +break; +case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; +break; +case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]); +break; +case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]); +break; +case 12: this.$ = $$[$0]; +break; +case 13: this.$ = $$[$0]; +break; +case 14: this.$ = new yy.ContentNode($$[$0]); +break; +case 15: this.$ = new yy.CommentNode($$[$0]); +break; +case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]); +break; +case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]); +break; +case 18: this.$ = $$[$0-1]; +break; +case 19: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]); +break; +case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true); +break; +case 21: this.$ = new yy.PartialNode($$[$0-1]); +break; +case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]); +break; +case 23: +break; +case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]; +break; +case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null]; +break; +case 26: this.$ = [[$$[$0-1]], $$[$0]]; +break; +case 27: this.$ = [[$$[$0]], null]; +break; +case 28: this.$ = [[new yy.DataNode($$[$0])], null]; +break; +case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; +break; +case 30: this.$ = [$$[$0]]; +break; +case 31: this.$ = $$[$0]; +break; +case 32: this.$ = new yy.StringNode($$[$0]); +break; +case 33: this.$ = new yy.IntegerNode($$[$0]); +break; +case 34: this.$ = new yy.BooleanNode($$[$0]); +break; +case 35: this.$ = new yy.DataNode($$[$0]); +break; +case 36: this.$ = new yy.HashNode($$[$0]); +break; +case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; +break; +case 38: this.$ = [$$[$0]]; +break; +case 39: this.$ = [$$[$0-2], $$[$0]]; +break; +case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])]; +break; +case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])]; +break; +case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])]; +break; +case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])]; +break; +case 44: this.$ = new yy.PartialNameNode($$[$0]); +break; +case 45: this.$ = new yy.IdNode($$[$0]); +break; +case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2]; +break; +case 47: this.$ = [$$[$0]]; +break; +} +}, +table: [{3:1,4:2,5:[2,7],6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],22:[1,14],23:[1,15],24:[1,16]},{1:[3]},{5:[1,17]},{5:[2,6],7:18,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,6],22:[1,14],23:[1,15],24:[1,16]},{5:[2,5],6:20,8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,5],22:[1,14],23:[1,15],24:[1,16]},{17:23,18:[1,22],21:24,28:[1,25],35:[1,27],38:26},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{4:28,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{4:29,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{17:30,21:24,28:[1,25],35:[1,27],38:26},{17:31,21:24,28:[1,25],35:[1,27],38:26},{17:32,21:24,28:[1,25],35:[1,27],38:26},{25:33,37:[1,34]},{1:[2,1]},{5:[2,2],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,2],22:[1,14],23:[1,15],24:[1,16]},{17:23,21:24,28:[1,25],35:[1,27],38:26},{5:[2,4],7:35,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,4],22:[1,14],23:[1,15],24:[1,16]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,23],14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],24:[2,23]},{18:[1,36]},{18:[2,27],21:41,26:37,27:38,28:[1,45],29:39,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,28]},{18:[2,45],28:[2,45],30:[2,45],31:[2,45],32:[2,45],35:[2,45],39:[1,48]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],39:[2,47]},{10:49,20:[1,50]},{10:51,20:[1,50]},{18:[1,52]},{18:[1,53]},{18:[1,54]},{18:[1,55],21:56,35:[1,27],38:26},{18:[2,44],35:[2,44]},{5:[2,3],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,3],22:[1,14],23:[1,15],24:[1,16]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{18:[2,25],21:41,27:57,28:[1,45],29:58,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,26]},{18:[2,30],28:[2,30],30:[2,30],31:[2,30],32:[2,30],35:[2,30]},{18:[2,36],34:59,35:[1,60]},{18:[2,31],28:[2,31],30:[2,31],31:[2,31],32:[2,31],35:[2,31]},{18:[2,32],28:[2,32],30:[2,32],31:[2,32],32:[2,32],35:[2,32]},{18:[2,33],28:[2,33],30:[2,33],31:[2,33],32:[2,33],35:[2,33]},{18:[2,34],28:[2,34],30:[2,34],31:[2,34],32:[2,34],35:[2,34]},{18:[2,35],28:[2,35],30:[2,35],31:[2,35],32:[2,35],35:[2,35]},{18:[2,38],35:[2,38]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],36:[1,61],39:[2,47]},{35:[1,62]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{21:63,35:[1,27],38:26},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],24:[2,21]},{18:[1,64]},{18:[2,24]},{18:[2,29],28:[2,29],30:[2,29],31:[2,29],32:[2,29],35:[2,29]},{18:[2,37],35:[2,37]},{36:[1,61]},{21:65,28:[1,69],30:[1,66],31:[1,67],32:[1,68],35:[1,27],38:26},{18:[2,46],28:[2,46],30:[2,46],31:[2,46],32:[2,46],35:[2,46],39:[2,46]},{18:[1,70]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],24:[2,22]},{18:[2,39],35:[2,39]},{18:[2,40],35:[2,40]},{18:[2,41],35:[2,41]},{18:[2,42],35:[2,42]},{18:[2,43],35:[2,43]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]}], +defaultActions: {17:[2,1],25:[2,28],38:[2,26],57:[2,24]}, +parseError: function parseError(str, hash) { + throw new Error(str); +}, +parse: function parse(input) { + var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") + this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) + if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +} +}; +/* Jison generated lexer */ +var lexer = (function(){ +var lexer = ({EOF:1, +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, +setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + if (this.options.ranges) this.yylloc.range = [0,0]; + this.offset = 0; + return this; + }, +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length-len-1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length-1); + this.matched = this.matched.substr(0, this.matched.length-1); + + if (lines.length-1) this.yylineno -= lines.length-1; + var r = this.yylloc.range; + + this.yylloc = {first_line: this.yylloc.first_line, + last_line: this.yylineno+1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, +more:function () { + this._more = true; + return this; + }, +less:function (n) { + this.unput(this.match.slice(n)); + }, +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, +lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, +begin:function begin(condition) { + this.conditionStack.push(condition); + }, +popState:function popState() { + return this.conditionStack.pop(); + }, +_currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, +topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, +pushState:function begin(condition) { + this.begin(condition); + }}); +lexer.options = {}; +lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START +switch($avoiding_name_collisions) { +case 0: yy_.yytext = "\\"; return 14; +break; +case 1: + if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); + if(yy_.yytext) return 14; + +break; +case 2: return 14; +break; +case 3: + if(yy_.yytext.slice(-1) !== "\\") this.popState(); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); + return 14; + +break; +case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; +break; +case 5: this.begin("par"); return 24; +break; +case 6: return 16; +break; +case 7: return 20; +break; +case 8: return 19; +break; +case 9: return 19; +break; +case 10: return 23; +break; +case 11: return 23; +break; +case 12: this.popState(); this.begin('com'); +break; +case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; +break; +case 14: return 22; +break; +case 15: return 36; +break; +case 16: return 35; +break; +case 17: return 35; +break; +case 18: return 39; +break; +case 19: /*ignore whitespace*/ +break; +case 20: this.popState(); return 18; +break; +case 21: this.popState(); return 18; +break; +case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; +break; +case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; +break; +case 24: yy_.yytext = yy_.yytext.substr(1); return 28; +break; +case 25: return 32; +break; +case 26: return 32; +break; +case 27: return 31; +break; +case 28: return 35; +break; +case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; +break; +case 30: return 'INVALID'; +break; +case 31: /*ignore whitespace*/ +break; +case 32: this.popState(); return 37; +break; +case 33: return 5; +break; +} +}; +lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:\-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/]+)/,/^(?:$)/]; +lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}}; +return lexer;})() +parser.lexer = lexer; +function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})();; +// lib/handlebars/compiler/base.js + +Handlebars.Parser = handlebars; + +Handlebars.parse = function(input) { + + // Just return if an already-compile AST was passed in. + if(input.constructor === Handlebars.AST.ProgramNode) { return input; } + + Handlebars.Parser.yy = Handlebars.AST; + return Handlebars.Parser.parse(input); +}; +; +// lib/handlebars/compiler/ast.js +Handlebars.AST = {}; + +Handlebars.AST.ProgramNode = function(statements, inverse) { + this.type = "program"; + this.statements = statements; + if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } +}; + +Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { + this.type = "mustache"; + this.escaped = !unescaped; + this.hash = hash; + + var id = this.id = rawParams[0]; + var params = this.params = rawParams.slice(1); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var eligibleHelper = this.eligibleHelper = id.isSimple; + + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + this.isHelper = eligibleHelper && (params.length || hash); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. +}; + +Handlebars.AST.PartialNode = function(partialName, context) { + this.type = "partial"; + this.partialName = partialName; + this.context = context; +}; + +Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { + var verifyMatch = function(open, close) { + if(open.original !== close.original) { + throw new Handlebars.Exception(open.original + " doesn't match " + close.original); + } + }; + + verifyMatch(mustache.id, close); + this.type = "block"; + this.mustache = mustache; + this.program = program; + this.inverse = inverse; + + if (this.inverse && !this.program) { + this.isInverse = true; + } +}; + +Handlebars.AST.ContentNode = function(string) { + this.type = "content"; + this.string = string; +}; + +Handlebars.AST.HashNode = function(pairs) { + this.type = "hash"; + this.pairs = pairs; +}; + +Handlebars.AST.IdNode = function(parts) { + this.type = "ID"; + this.original = parts.join("."); + + var dig = [], depth = 0; + + for(var i=0,l=parts.length; i<l; i++) { + var part = parts[i]; + + if (part === ".." || part === "." || part === "this") { + if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); } + else if (part === "..") { depth++; } + else { this.isScoped = true; } + } + else { dig.push(part); } + } + + this.parts = dig; + this.string = dig.join('.'); + this.depth = depth; + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; + + this.stringModeValue = this.string; +}; + +Handlebars.AST.PartialNameNode = function(name) { + this.type = "PARTIAL_NAME"; + this.name = name; +}; + +Handlebars.AST.DataNode = function(id) { + this.type = "DATA"; + this.id = id; +}; + +Handlebars.AST.StringNode = function(string) { + this.type = "STRING"; + this.string = string; + this.stringModeValue = string; +}; + +Handlebars.AST.IntegerNode = function(integer) { + this.type = "INTEGER"; + this.integer = integer; + this.stringModeValue = Number(integer); +}; + +Handlebars.AST.BooleanNode = function(bool) { + this.type = "BOOLEAN"; + this.bool = bool; + this.stringModeValue = bool === "true"; +}; + +Handlebars.AST.CommentNode = function(comment) { + this.type = "comment"; + this.comment = comment; +}; +; +// lib/handlebars/utils.js + +var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + +Handlebars.Exception = function(message) { + var tmp = Error.prototype.constructor.apply(this, arguments); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } +}; +Handlebars.Exception.prototype = new Error(); + +// Build out our basic SafeString type +Handlebars.SafeString = function(string) { + this.string = string; +}; +Handlebars.SafeString.prototype.toString = function() { + return this.string.toString(); +}; + +var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" +}; + +var badChars = /[&<>"'`]/g; +var possible = /[&<>"'`]/; + +var escapeChar = function(chr) { + return escape[chr] || "&"; +}; + +Handlebars.Utils = { + extend: function(obj, value) { + for(var key in value) { + if(value.hasOwnProperty(key)) { + obj[key] = value[key]; + } + } + }, + + escapeExpression: function(string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof Handlebars.SafeString) { + return string.toString(); + } else if (string == null || string === false) { + return ""; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = string.toString(); + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + }, + + isEmpty: function(value) { + if (!value && value !== 0) { + return true; + } else if(toString.call(value) === "[object Array]" && value.length === 0) { + return true; + } else { + return false; + } + } +}; +; +// lib/handlebars/compiler/compiler.js + +/*jshint eqnull:true*/ +var Compiler = Handlebars.Compiler = function() {}; +var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; + +// the foundHelper register will disambiguate helper lookup from finding a +// function in a context. This is necessary for mustache compatibility, which +// requires that context functions in blocks are evaluated by blockHelperMissing, +// and then proceed as if the resulting value was provided to blockHelperMissing. + +Compiler.prototype = { + compiler: Compiler, + + disassemble: function() { + var opcodes = this.opcodes, opcode, out = [], params, param; + + for (var i=0, l=opcodes.length; i<l; i++) { + opcode = opcodes[i]; + + if (opcode.opcode === 'DECLARE') { + out.push("DECLARE " + opcode.name + "=" + opcode.value); + } else { + params = []; + for (var j=0; j<opcode.args.length; j++) { + param = opcode.args[j]; + if (typeof param === "string") { + param = "\"" + param.replace("\n", "\\n") + "\""; + } + params.push(param); + } + out.push(opcode.opcode + " " + params.join(" ")); + } + } + + return out.join("\n"); + }, + equals: function(other) { + var len = this.opcodes.length; + if (other.opcodes.length !== len) { + return false; + } + + for (var i = 0; i < len; i++) { + var opcode = this.opcodes[i], + otherOpcode = other.opcodes[i]; + if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) { + return false; + } + for (var j = 0; j < opcode.args.length; j++) { + if (opcode.args[j] !== otherOpcode.args[j]) { + return false; + } + } + } + + len = this.children.length; + if (other.children.length !== len) { + return false; + } + for (i = 0; i < len; i++) { + if (!this.children[i].equals(other.children[i])) { + return false; + } + } + + return true; + }, + + guid: 0, + + compile: function(program, options) { + this.children = []; + this.depths = {list: []}; + this.options = options; + + // These changes will propagate to the other compiler components + var knownHelpers = this.options.knownHelpers; + this.options.knownHelpers = { + 'helperMissing': true, + 'blockHelperMissing': true, + 'each': true, + 'if': true, + 'unless': true, + 'with': true, + 'log': true + }; + if (knownHelpers) { + for (var name in knownHelpers) { + this.options.knownHelpers[name] = knownHelpers[name]; + } + } + + return this.program(program); + }, + + accept: function(node) { + return this[node.type](node); + }, + + program: function(program) { + var statements = program.statements, statement; + this.opcodes = []; + + for(var i=0, l=statements.length; i<l; i++) { + statement = statements[i]; + this[statement.type](statement); + } + this.isSimple = l === 1; + + this.depths.list = this.depths.list.sort(function(a, b) { + return a - b; + }); + + return this; + }, + + compileProgram: function(program) { + var result = new this.compiler().compile(program, this.options); + var guid = this.guid++, depth; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + + for(var i=0, l=result.depths.list.length; i<l; i++) { + depth = result.depths.list[i]; + + if(depth < 2) { continue; } + else { this.addDepth(depth - 1); } + } + + return guid; + }, + + block: function(block) { + var mustache = block.mustache, + program = block.program, + inverse = block.inverse; + + if (program) { + program = this.compileProgram(program); + } + + if (inverse) { + inverse = this.compileProgram(inverse); + } + + var type = this.classifyMustache(mustache); + + if (type === "helper") { + this.helperMustache(mustache, program, inverse); + } else if (type === "simple") { + this.simpleMustache(mustache); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('blockValue'); + } else { + this.ambiguousMustache(mustache, program, inverse); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('ambiguousBlockValue'); + } + + this.opcode('append'); + }, + + hash: function(hash) { + var pairs = hash.pairs, pair, val; + + this.opcode('pushHash'); + + for(var i=0, l=pairs.length; i<l; i++) { + pair = pairs[i]; + val = pair[1]; + + if (this.options.stringParams) { + if(val.depth) { + this.addDepth(val.depth); + } + this.opcode('getContext', val.depth || 0); + this.opcode('pushStringParam', val.stringModeValue, val.type); + } else { + this.accept(val); + } + + this.opcode('assignToHash', pair[0]); + } + this.opcode('popHash'); + }, + + partial: function(partial) { + var partialName = partial.partialName; + this.usePartial = true; + + if(partial.context) { + this.ID(partial.context); + } else { + this.opcode('push', 'depth0'); + } + + this.opcode('invokePartial', partialName.name); + this.opcode('append'); + }, + + content: function(content) { + this.opcode('appendContent', content.string); + }, + + mustache: function(mustache) { + var options = this.options; + var type = this.classifyMustache(mustache); + + if (type === "simple") { + this.simpleMustache(mustache); + } else if (type === "helper") { + this.helperMustache(mustache); + } else { + this.ambiguousMustache(mustache); + } + + if(mustache.escaped && !options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + + ambiguousMustache: function(mustache, program, inverse) { + var id = mustache.id, + name = id.parts[0], + isBlock = program != null || inverse != null; + + this.opcode('getContext', id.depth); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + this.opcode('invokeAmbiguous', name, isBlock); + }, + + simpleMustache: function(mustache) { + var id = mustache.id; + + if (id.type === 'DATA') { + this.DATA(id); + } else if (id.parts.length) { + this.ID(id); + } else { + // Simplified ID for `this` + this.addDepth(id.depth); + this.opcode('getContext', id.depth); + this.opcode('pushContext'); + } + + this.opcode('resolvePossibleLambda'); + }, + + helperMustache: function(mustache, program, inverse) { + var params = this.setupFullMustacheParams(mustache, program, inverse), + name = mustache.id.parts[0]; + + if (this.options.knownHelpers[name]) { + this.opcode('invokeKnownHelper', params.length, name); + } else if (this.options.knownHelpersOnly) { + throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name); + } else { + this.opcode('invokeHelper', params.length, name); + } + }, + + ID: function(id) { + this.addDepth(id.depth); + this.opcode('getContext', id.depth); + + var name = id.parts[0]; + if (!name) { + this.opcode('pushContext'); + } else { + this.opcode('lookupOnContext', id.parts[0]); + } + + for(var i=1, l=id.parts.length; i<l; i++) { + this.opcode('lookup', id.parts[i]); + } + }, + + DATA: function(data) { + this.options.data = true; + this.opcode('lookupData', data.id); + }, + + STRING: function(string) { + this.opcode('pushString', string.string); + }, + + INTEGER: function(integer) { + this.opcode('pushLiteral', integer.integer); + }, + + BOOLEAN: function(bool) { + this.opcode('pushLiteral', bool.bool); + }, + + comment: function() {}, + + // HELPERS + opcode: function(name) { + this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) }); + }, + + declare: function(name, value) { + this.opcodes.push({ opcode: 'DECLARE', name: name, value: value }); + }, + + addDepth: function(depth) { + if(isNaN(depth)) { throw new Error("EWOT"); } + if(depth === 0) { return; } + + if(!this.depths[depth]) { + this.depths[depth] = true; + this.depths.list.push(depth); + } + }, + + classifyMustache: function(mustache) { + var isHelper = mustache.isHelper; + var isEligible = mustache.eligibleHelper; + var options = this.options; + + // if ambiguous, we can possibly resolve the ambiguity now + if (isEligible && !isHelper) { + var name = mustache.id.parts[0]; + + if (options.knownHelpers[name]) { + isHelper = true; + } else if (options.knownHelpersOnly) { + isEligible = false; + } + } + + if (isHelper) { return "helper"; } + else if (isEligible) { return "ambiguous"; } + else { return "simple"; } + }, + + pushParams: function(params) { + var i = params.length, param; + + while(i--) { + param = params[i]; + + if(this.options.stringParams) { + if(param.depth) { + this.addDepth(param.depth); + } + + this.opcode('getContext', param.depth || 0); + this.opcode('pushStringParam', param.stringModeValue, param.type); + } else { + this[param.type](param); + } + } + }, + + setupMustacheParams: function(mustache) { + var params = mustache.params; + this.pushParams(params); + + if(mustache.hash) { + this.hash(mustache.hash); + } else { + this.opcode('emptyHash'); + } + + return params; + }, + + // this will replace setupMustacheParams when we're done + setupFullMustacheParams: function(mustache, program, inverse) { + var params = mustache.params; + this.pushParams(params); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + if(mustache.hash) { + this.hash(mustache.hash); + } else { + this.opcode('emptyHash'); + } + + return params; + } +}; + +var Literal = function(value) { + this.value = value; +}; + +JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function(parent, name /* , type*/) { + if (/^[0-9]+$/.test(name)) { + return parent + "[" + name + "]"; + } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return parent + "." + name; + } + else { + return parent + "['" + name + "']"; + } + }, + + appendToBuffer: function(string) { + if (this.environment.isSimple) { + return "return " + string + ";"; + } else { + return { + appendToBuffer: true, + content: string, + toString: function() { return "buffer += " + string + ";"; } + }; + } + }, + + initializeBuffer: function() { + return this.quotedString(""); + }, + + namespace: "Handlebars", + // END PUBLIC API + + compile: function(environment, options, context, asObject) { + this.environment = environment; + this.options = options || {}; + + Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n"); + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + programs: [], + environments: [], + aliases: { } + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.registers = { list: [] }; + this.compileStack = []; + this.inlineStack = []; + + this.compileChildren(environment, options); + + var opcodes = environment.opcodes, opcode; + + this.i = 0; + + for(l=opcodes.length; this.i<l; this.i++) { + opcode = opcodes[this.i]; + + if(opcode.opcode === 'DECLARE') { + this[opcode.name] = opcode.value; + } else { + this[opcode.opcode].apply(this, opcode.args); + } + } + + return this.createFunctionContext(asObject); + }, + + nextOpcode: function() { + var opcodes = this.environment.opcodes; + return opcodes[this.i + 1]; + }, + + eat: function() { + this.i = this.i + 1; + }, + + preamble: function() { + var out = []; + + if (!this.isChild) { + var namespace = this.namespace; + var copies = "helpers = helpers || " + namespace + ".helpers;"; + if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; } + if (this.options.data) { copies = copies + " data = data || {};"; } + out.push(copies); + } else { + out.push(''); + } + + if (!this.environment.isSimple) { + out.push(", buffer = " + this.initializeBuffer()); + } else { + out.push(""); + } + + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = out; + }, + + createFunctionContext: function(asObject) { + var locals = this.stackVars.concat(this.registers.list); + + if(locals.length > 0) { + this.source[1] = this.source[1] + ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + for (var alias in this.context.aliases) { + this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + + if (this.source[1]) { + this.source[1] = "var " + this.source[1].substring(2) + ";"; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.source.push("return buffer;"); + } + + var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; + + for(var i=0, l=this.environment.depths.list.length; i<l; i++) { + params.push("depth" + this.environment.depths.list[i]); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(); + + if (!this.isChild) { + var revision = Handlebars.COMPILER_REVISION, + versions = Handlebars.REVISION_CHANGES[revision]; + source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source; + } + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}'; + Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n"); + return functionSource; + } + }, + mergeSource: function() { + // WARN: We are not handling the case where buffer is still populated as the source should + // not have buffer append operations as their final action. + var source = '', + buffer; + for (var i = 0, len = this.source.length; i < len; i++) { + var line = this.source[i]; + if (line.appendToBuffer) { + if (buffer) { + buffer = buffer + '\n + ' + line.content; + } else { + buffer = line.content; + } + } else { + if (buffer) { + source += 'buffer += ' + buffer + ';\n '; + buffer = undefined; + } + source += line + '\n '; + } + } + return source; + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function() { + this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + + var params = ["depth0"]; + this.setupParams(0, params); + + this.replaceStack(function(current) { + params.splice(1, 0, current); + return "blockHelperMissing.call(" + params.join(", ") + ")"; + }); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function() { + this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing'; + + var params = ["depth0"]; + this.setupParams(0, params); + + var current = this.topStack(); + params.splice(1, 0, current); + + // Use the options value generated from the invocation + params[params.length-1] = 'options'; + + this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }"); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function(content) { + this.source.push(this.appendToBuffer(this.quotedString(content))); + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function() { + // Force anything that is inlined onto the stack so we don't have duplication + // when we examine local + this.flushInline(); + var local = this.popStack(); + this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }"); + if (this.environment.isSimple) { + this.source.push("else { " + this.appendToBuffer("''") + " }"); + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function() { + this.context.aliases.escapeExpression = 'this.escapeExpression'; + + this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")")); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function(depth) { + if(this.lastContext !== depth) { + this.lastContext = depth; + } + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function(name) { + this.push(this.nameLookup('depth' + this.lastContext, name, 'context')); + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function() { + this.pushStackLiteral('depth' + this.lastContext); + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function() { + this.context.aliases.functionType = '"function"'; + + this.replaceStack(function(current) { + return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current; + }); + }, + + // [lookup] + // + // On stack, before: value, ... + // On stack, after: value[name], ... + // + // Replace the value on the stack with the result of looking + // up `name` on `value` + lookup: function(name) { + this.replaceStack(function(current) { + return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context'); + }); + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data[id], ... + // + // Push the result of looking up `id` on the current data + lookupData: function(id) { + this.push(this.nameLookup('data', id, 'data')); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function(string, type) { + this.pushStackLiteral('depth' + this.lastContext); + + this.pushString(type); + + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + }, + + emptyHash: function() { + this.pushStackLiteral('{}'); + + if (this.options.stringParams) { + this.register('hashTypes', '{}'); + this.register('hashContexts', '{}'); + } + }, + pushHash: function() { + this.hash = {values: [], types: [], contexts: []}; + }, + popHash: function() { + var hash = this.hash; + this.hash = undefined; + + if (this.options.stringParams) { + this.register('hashContexts', '{' + hash.contexts.join(',') + '}'); + this.register('hashTypes', '{' + hash.types.join(',') + '}'); + } + this.push('{\n ' + hash.values.join(',\n ') + '\n }'); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [push] + // + // On stack, before: ... + // On stack, after: expr, ... + // + // Push an expression onto the stack + push: function(expr) { + this.inlineStack.push(expr); + return expr; + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function(paramSize, name) { + this.context.aliases.helperMissing = 'helpers.helperMissing'; + + var helper = this.lastHelper = this.setupHelper(paramSize, name, true); + + this.push(helper.name); + this.replaceStack(function(name) { + return name + ' ? ' + name + '.call(' + + helper.callParams + ") " + ": helperMissing.call(" + + helper.helperMissingParams + ")"; + }); + }, + + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(helper.name + ".call(" + helper.callParams + ")"); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function(name, helperCall) { + this.context.aliases.functionType = '"function"'; + + this.pushStackLiteral('{}'); // Hash value + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context'); + var nextStack = this.nextStack(); + + this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }'); + this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }'); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function(name) { + var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"]; + + if (this.options.data) { + params.push("data"); + } + + this.context.aliases.self = "this"; + this.push("self.invokePartial(" + params.join(", ") + ")"); + }, + + // [assignToHash] + // + // On stack, before: value, hash, ... + // On stack, after: hash, ... + // + // Pops a value and hash off the stack, assigns `hash[key] = value` + // and pushes the hash back onto the stack. + assignToHash: function(key) { + var value = this.popStack(), + context, + type; + + if (this.options.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts.push("'" + key + "': " + context); + } + if (type) { + hash.types.push("'" + key + "': " + type); + } + hash.values.push("'" + key + "': (" + value + ")"); + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function(environment, options) { + var children = environment.children, child, compiler; + + for(var i=0, l=children.length; i<l; i++) { + child = children[i]; + compiler = new this.compiler(); + + var index = this.matchExistingProgram(child); + + if (index == null) { + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile(child, options, this.context); + this.context.environments[index] = child; + } else { + child.index = index; + child.name = 'program' + index; + } + } + }, + matchExistingProgram: function(child) { + for (var i = 0, len = this.context.environments.length; i < len; i++) { + var environment = this.context.environments[i]; + if (environment && environment.equals(child)) { + return i; + } + } + }, + + programExpression: function(guid) { + this.context.aliases.self = "this"; + + if(guid == null) { + return "self.noop"; + } + + var child = this.environment.children[guid], + depths = child.depths.list, depth; + + var programParams = [child.index, child.name, "data"]; + + for(var i=0, l = depths.length; i<l; i++) { + depth = depths[i]; + + if(depth === 1) { programParams.push("depth0"); } + else { programParams.push("depth" + (depth - 1)); } + } + + return (depths.length === 0 ? "self.program(" : "self.programWithDepth(") + programParams.join(", ") + ")"; + }, + + register: function(name, val) { + this.useRegister(name); + this.source.push(name + " = " + val + ";"); + }, + + useRegister: function(name) { + if(!this.registers[name]) { + this.registers[name] = true; + this.registers.list.push(name); + } + }, + + pushStackLiteral: function(item) { + return this.push(new Literal(item)); + }, + + pushStack: function(item) { + this.flushInline(); + + var stack = this.incrStack(); + if (item) { + this.source.push(stack + " = " + item + ";"); + } + this.compileStack.push(stack); + return stack; + }, + + replaceStack: function(callback) { + var prefix = '', + inline = this.isInline(), + stack; + + // If we are currently inline then we want to merge the inline statement into the + // replacement statement via ',' + if (inline) { + var top = this.popStack(true); + + if (top instanceof Literal) { + // Literals do not need to be inlined + stack = top.value; + } else { + // Get or create the current stack name for use by the inline + var name = this.stackSlot ? this.topStackName() : this.incrStack(); + + prefix = '(' + this.push(name) + ' = ' + top + '),'; + stack = this.topStack(); + } + } else { + stack = this.topStack(); + } + + var item = callback.call(this, stack); + + if (inline) { + if (this.inlineStack.length || this.compileStack.length) { + this.popStack(); + } + this.push('(' + prefix + item + ')'); + } else { + // Prevent modification of the context depth variable. Through replaceStack + if (!/^stack/.test(stack)) { + stack = this.nextStack(); + } + + this.source.push(stack + " = (" + prefix + item + ");"); + } + return stack; + }, + + nextStack: function() { + return this.pushStack(); + }, + + incrStack: function() { + this.stackSlot++; + if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return this.topStackName(); + }, + topStackName: function() { + return "stack" + this.stackSlot; + }, + flushInline: function() { + var inlineStack = this.inlineStack; + if (inlineStack.length) { + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + this.pushStack(entry); + } + } + } + }, + isInline: function() { + return this.inlineStack.length; + }, + + popStack: function(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + if (!inline) { + this.stackSlot--; + } + return item; + } + }, + + topStack: function(wrapped) { + var stack = (this.isInline() ? this.inlineStack : this.compileStack), + item = stack[stack.length - 1]; + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + return item; + } + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + setupHelper: function(paramSize, name, missingParams) { + var params = []; + this.setupParams(paramSize, params, missingParams); + var foundHelper = this.nameLookup('helpers', name, 'helper'); + + return { + params: params, + name: foundHelper, + callParams: ["depth0"].concat(params).join(", "), + helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") + }; + }, + + // the params and contexts arguments are passed in arrays + // to fill in + setupParams: function(paramSize, params, useRegister) { + var options = [], contexts = [], types = [], param, inverse, program; + + options.push("hash:" + this.popStack()); + + inverse = this.popStack(); + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + if (!program) { + this.context.aliases.self = "this"; + program = "self.noop"; + } + + if (!inverse) { + this.context.aliases.self = "this"; + inverse = "self.noop"; + } + + options.push("inverse:" + inverse); + options.push("fn:" + program); + } + + for(var i=0; i<paramSize; i++) { + param = this.popStack(); + params.push(param); + + if(this.options.stringParams) { + types.push(this.popStack()); + contexts.push(this.popStack()); + } + } + + if (this.options.stringParams) { + options.push("contexts:[" + contexts.join(",") + "]"); + options.push("types:[" + types.join(",") + "]"); + options.push("hashContexts:hashContexts"); + options.push("hashTypes:hashTypes"); + } + + if(this.options.data) { + options.push("data:data"); + } + + options = "{" + options.join(",") + "}"; + if (useRegister) { + this.register('options', options); + params.push('options'); + } else { + params.push(options); + } + return params.join(", "); + } +}; + +var reservedWords = ( + "break else new var" + + " case finally return void" + + " catch for switch while" + + " continue function this with" + + " default if throw" + + " delete in try" + + " do instanceof typeof" + + " abstract enum int short" + + " boolean export interface static" + + " byte extends long super" + + " char final native synchronized" + + " class float package throws" + + " const goto private transient" + + " debugger implements protected volatile" + + " double import public let yield" +).split(" "); + +var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + +for(var i=0, l=reservedWords.length; i<l; i++) { + compilerWords[reservedWords[i]] = true; +} + +JavaScriptCompiler.isValidJavaScriptVariableName = function(name) { + if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) { + return true; + } + return false; +}; + +Handlebars.precompile = function(input, options) { + if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) { + throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.precompile. You passed " + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + var ast = Handlebars.parse(input); + var environment = new Compiler().compile(ast, options); + return new JavaScriptCompiler().compile(environment, options); +}; + +Handlebars.compile = function(input, options) { + if (input == null || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) { + throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + var compiled; + function compile() { + var ast = Handlebars.parse(input); + var environment = new Compiler().compile(ast, options); + var templateSpec = new JavaScriptCompiler().compile(environment, options, undefined, true); + return Handlebars.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + return function(context, options) { + if (!compiled) { + compiled = compile(); + } + return compiled.call(this, context, options); + }; +}; + +; +// lib/handlebars/runtime.js + +Handlebars.VM = { + template: function(templateSpec) { + // Just add water + var container = { + escapeExpression: Handlebars.Utils.escapeExpression, + invokePartial: Handlebars.VM.invokePartial, + programs: [], + program: function(i, fn, data) { + var programWrapper = this.programs[i]; + if(data) { + programWrapper = Handlebars.VM.program(i, fn, data); + } else if (!programWrapper) { + programWrapper = this.programs[i] = Handlebars.VM.program(i, fn); + } + return programWrapper; + }, + programWithDepth: Handlebars.VM.programWithDepth, + noop: Handlebars.VM.noop, + compilerInfo: null + }; + + return function(context, options) { + options = options || {}; + var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data); + + var compilerInfo = container.compilerInfo || [], + compilerRevision = compilerInfo[0] || 1, + currentRevision = Handlebars.COMPILER_REVISION; + + if (compilerRevision !== currentRevision) { + if (compilerRevision < currentRevision) { + var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision], + compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision]; + throw "Template was precompiled with an older version of Handlebars than the current runtime. "+ + "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."; + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+ + "Please update your runtime to a newer version ("+compilerInfo[1]+")."; + } + } + + return result; + }; + }, + + programWithDepth: function(i, fn, data /*, $depth */) { + var args = Array.prototype.slice.call(arguments, 3); + + var program = function(context, options) { + options = options || {}; + + return fn.apply(this, [context, options.data || data].concat(args)); + }; + program.program = i; + program.depth = args.length; + return program; + }, + program: function(i, fn, data) { + var program = function(context, options) { + options = options || {}; + + return fn(context, options.data || data); + }; + program.program = i; + program.depth = 0; + return program; + }, + noop: function() { return ""; }, + invokePartial: function(partial, name, context, helpers, partials, data) { + var options = { helpers: helpers, partials: partials, data: data }; + + if(partial === undefined) { + throw new Handlebars.Exception("The partial " + name + " could not be found"); + } else if(partial instanceof Function) { + return partial(context, options); + } else if (!Handlebars.compile) { + throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); + } else { + partials[name] = Handlebars.compile(partial, {data: data !== undefined}); + return partials[name](context, options); + } + } +}; + +Handlebars.template = Handlebars.VM.template; +; +// lib/handlebars/browser-suffix.js +})(Handlebars); +; diff --git a/js/libs/jquery-1.9.1.js b/js/libs/jquery-1.9.1.js new file mode 100755 index 0000000..e2c203f --- /dev/null +++ b/js/libs/jquery-1.9.1.js @@ -0,0 +1,9597 @@ +/*! + * jQuery JavaScript Library v1.9.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-2-4 + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<9 + // For `typeof node.method` instead of `node.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.9.1", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, all, a, + input, select, fragment, + opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + checkOn: !!input.value, + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: document.compatMode === "CSS1Compat", + + // Will be defined later + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "<div></div>"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})(); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var i, l, thisCache, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + // Try to fetch any internally stored data first + return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + } + + this.each(function() { + jQuery.data( this, key, value ); + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + hooks.cur = fn; + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, notxml, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + // In IE9+, Flash objects don't have .getAttribute (#12945) + // Support: IE9+ + if ( typeof elem.getAttribute !== core_strundefined ) { + ret = elem.getAttribute( name ); + } + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( rboolean.test( name ) ) { + // Set corresponding property to false for boolean attributes + // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8 + if ( !getSetAttribute && ruseDefault.test( name ) ) { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } else { + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + var + // Use .prop to determine if this attribute is understood as boolean + prop = jQuery.prop( elem, name ), + + // Fetch it accordingly + attr = typeof prop === "boolean" && elem.getAttribute( name ), + detail = typeof prop === "boolean" ? + + getSetInput && getSetAttribute ? + attr != null : + // oldIE fabricates an empty string for missing boolean attributes + // and conflates checked/selected into attroperties + ruseDefault.test( name ) ? + elem[ jQuery.camelCase( "default-" + name ) ] : + !!attr : + + // fetch an attribute node for properties not recognized as boolean + elem.getAttributeNode( name ); + + return detail && detail.value !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; + +// fix oldIE value attroperty +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return jQuery.nodeName( elem, "input" ) ? + + // Ignore the value *property* by using defaultValue + elem.defaultValue : + + ret && ret.specified ? ret.value : undefined; + }, + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret == null ? undefined : ret; + } + }); + }); + + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + event.isTrigger = true; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG <use> instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur != this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + } + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== document.activeElement && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === document.activeElement && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var i, + cachedruns, + Expr, + getText, + isXML, + compile, + hasDuplicate, + outermostContext, + + // Local document vars + setDocument, + document, + docElem, + documentIsXML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + sortOrder, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + support = {}, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Array methods + arr = [], + pop = arr.pop, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rsibling = /[\x20\t\r\n\f]*[+~]/, + + rnative = /^[^{]+\{\s*\[native code/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g, + funescape = function( _, escaped ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + return high !== high ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Use a stripped-down slice if we can't use a native one +try { + slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + while ( (elem = this[i++]) ) { + results.push( elem ); + } + return results; + }; +} + +/** + * For feature detection + * @param {Function} fn The function to test for native support + */ +function isNative( fn ) { + return rnative.test( fn + "" ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var cache, + keys = []; + + return (cache = function( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + }); +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( !documentIsXML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + + // QSA path + if ( support.qsa && !rbuggyQSA.test(selector) ) { + old = true; + nid = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsXML = isXML( doc ); + + // Check if getElementsByTagName("*") returns only elements + support.tagNameNoComments = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if attributes should be retrieved by attribute nodes + support.attributes = assert(function( div ) { + div.innerHTML = "<select></select>"; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }); + + // Check if getElementsByClassName can be trusted + support.getByClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }); + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + support.getByName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = doc.getElementsByName && + // buggy browsers will return fewer than the correct 2 + doc.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + doc.getElementsByName( expando + 0 ).length; + support.getIdNotName = !doc.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + + // IE6/7 return modified attributes + Expr.attrHandle = assert(function( div ) { + div.innerHTML = "<a href='#'></a>"; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }) ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }; + + // ID find and filter + if ( support.getIdNotName ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.tagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Name + Expr.find["NAME"] = support.getByName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }; + + // Class + Expr.find["CLASS"] = support.getByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) { + return context.getElementsByClassName( className ); + } + }; + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21), + // no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ]; + + if ( (support.qsa = isNative(doc.querySelectorAll)) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = "<select><option selected=''></option></select>"; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE8 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "<input type='hidden' i=''/>"; + if ( div.querySelectorAll("[i^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = new RegExp( rbuggyMatches.join("|") ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + var compare; + + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) { + if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) { + if ( a === doc || contains( preferredDoc, a ) ) { + return -1; + } + if ( b === doc || contains( preferredDoc, b ) ) { + return 1; + } + return 0; + } + return compare & 4 ? -1 : 1; + } + + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + // Always assume the presence of duplicates if sort doesn't + // pass them to our comparison function (as in Google Chrome). + hasDuplicate = false; + [0, 0].sort( sortOrder ); + support.detectDuplicates = hasDuplicate; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyQSA always contains :focus, so no need for an existence check + if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + var val; + + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( !documentIsXML ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( documentIsXML || support.attributes ) { + return elem.getAttribute( name ); + } + return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ? + name : + val && val.specified ? val.value : null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[4] ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + + nodeName = nodeName.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifider + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsXML ? + elem.getAttribute("xml:lang") || elem.getAttribute("lang") : + elem.lang) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push( { + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !documentIsXML && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + documentIsXML, + results, + rsibling.test( selector ) + ); + return results; +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Easy API for creating new setFilters +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Initialize with the default document +setDocument(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, ret, self, + len = this.length; + + if ( typeof selector !== "string" ) { + self = this; + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + ret = []; + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, this[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = ( this.selector ? this.selector + " " : "" ) + selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true) ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /<tbody/i, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + manipulation_rcheckableType = /^(?:checkbox|radio)$/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "<select multiple='multiple'>", "</select>" ], + legend: [ 1, "<fieldset>", "</fieldset>" ], + area: [ 1, "<map>", "</map>" ], + param: [ 1, "<object>", "</object>" ], + thead: [ 1, "<table>", "</table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1></$2>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + var isFunc = jQuery.isFunction( value ); + + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( !isFunc && typeof value !== "string" ) { + value = jQuery( value ).not( this ).detach(); + } + + return this.domManip( [ value ], true, function( elem ) { + var next = this.nextSibling, + parent = this.parentNode; + + if ( parent ) { + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + }); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, table ? self.html() : undefined ); + } + self.domManip( args, table, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + node, + i + ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery.ajax({ + url: node.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + var attr = elem.getAttributeNode("type"); + elem.type = ( attr && attr.specified ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted <tbody> from table fragments + if ( !jQuery.support.tbody ) { + + // String was a <table>, *may* have spurious <tbody> + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare <thead> or <tfoot> + wrap[1] === "<table>" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + var bool = typeof state === "boolean"; + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("<iframe frameborder='0' width='0' height='0'/>") + .css( "cssText", "display:block !important" ) + ).appendTo( doc.documentElement ); + + // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse + doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document; + doc.write("<!doctype html><html><body>"); + doc.close(); + + display = actualDisplay( nodeName, doc ); + iframe.detach(); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + } + + return display; +} + +// Called ONLY from within css_defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + display = jQuery.css( elem[0], "display" ); + elem.remove(); + return display; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ? + jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }) : + getWidthOrHeight( elem, name, extra ); + } + }, + + set: function( elem, value, extra ) { + var styles = extra && getStyles( elem ); + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ) : 0 + ); + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + // if value === "", then remove inline opacity #12685 + if ( ( value >= 1 || value === "" ) && + jQuery.trim( filter.replace( ralpha, "" ) ) === "" && + style.removeAttribute ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there is no filter style applied in a css rule or unset inline opacity, we are done + if ( value === "" || currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +// These hooks cannot be added until DOM ready because the support test +// for it is not run until after DOM ready +jQuery(function() { + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + if ( computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, + curCSS, [ elem, "marginRight" ] ); + } + } + }; + } + + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // getComputedStyle returns percent when specified for top/left/bottom/right + // rather than make the css module depend on the offset module, we just check for it here + if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { + jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = { + get: function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + // if curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + }; + }); + } + +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + // Support: Opera <= 12.12 + // Opera reports offsetWidths and offsetHeights less than zero on some elements + return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 || + (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function(){ + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + }) + .filter(function(){ + var type = this.type; + // Use .is(":disabled") so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !manipulation_rcheckableType.test( type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +//Serialize an array of form elements or a set of +//key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // Item is non-scalar (array or object), encode its numeric index. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +}); + +jQuery.fn.hover = function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); +}; +var + // Document location + ajaxLocParts, + ajaxLocation, + ajax_nonce = jQuery.now(), + + ajax_rquery = /\?/, + rhash = /#.*$/, + rts = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat("*"); + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + while ( (dataType = dataTypes[i++]) ) { + // Prepend if requested + if ( dataType[0] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); + + // Otherwise append + } else { + (structure[ dataType ] = structure[ dataType ] || []).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + }); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var deep, key, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + var selector, response, type, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + }).complete( callback && function( jqXHR, status ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + }); + } + + return this; +}; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){ + jQuery.fn[ type ] = function( fn ){ + return this.on( type, fn ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + url: url, + type: method, + dataType: type, + data: data, + success: callback + }); + }; +}); + +jQuery.extend({ + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: ajaxLocation, + type: "GET", + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Cross-domain detection vars + parts, + // Loop variable + i, + // URL without anti-cache param + cacheURL, + // Response headers as string + responseHeadersString, + // timeout handle + timeoutTimer, + + // To know if global events are to be dispatched + fireGlobals, + + transport, + // Response headers + responseHeaders, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks("once memory"), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( (match = rheaders.exec( responseHeadersString )) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + var lname = name.toLowerCase(); + if ( !state ) { + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( state < 2 ) { + for ( code in map ) { + // Lazy-add the new callback in a way that preserves old ones + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } else { + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ).complete = completeDeferred.add; + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger("ajaxStart"); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + cacheURL = s.url; + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add anti-cache in url if needed + if ( s.cache === false ) { + s.url = rts.test( cacheURL ) ? + + // If there is already a '_' parameter, set its value + cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) : + + // Otherwise add one to the end + cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++; + } + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + } + + // aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout(function() { + jqXHR.abort("timeout"); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch ( e ) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader("etag"); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 ) { + isSuccess = true; + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + isSuccess = true; + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + isSuccess = ajaxConvert( s, response ); + statusText = isSuccess.state; + success = isSuccess.data; + error = isSuccess.error; + isSuccess = !error; + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger("ajaxStop"); + } + } + } + + return jqXHR; + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + } +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + var firstDataType, ct, finalDataType, type, + contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + var conv2, current, conv, tmp, + converters = {}, + i = 0, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(), + prev = dataTypes[ 0 ]; + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + // Convert to each sequential dataType, tolerating list modification + for ( ; (current = dataTypes[++i]); ) { + + // There's only work to do if current dataType is non-auto + if ( current !== "*" ) { + + // Convert response if prev dataType is non-auto and differs from current + if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split(" "); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.splice( i--, 0, current ); + } + + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s["throws"] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + + // Update prev for next iteration + prev = current; + } + } + + return { state: "success", data: response }; +} +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /(?:java|ecma)script/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || jQuery("head")[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement("script"); + + script.async = true; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( script.parentNode ) { + script.parentNode.removeChild( script ); + } + + // Dereference the script + script = null; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + + // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending + // Use native DOM manipulation to avoid our domManip AJAX trickery + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( undefined, true ); + } + } + }; + } +}); +var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); +var xhrCallbacks, xhrSupported, + xhrId = 0, + // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject && function() { + // Abort all pending requests + var key; + for ( key in xhrCallbacks ) { + xhrCallbacks[ key ]( undefined, true ); + } + }; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject("Microsoft.XMLHTTP"); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +xhrSupported = jQuery.ajaxSettings.xhr(); +jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +xhrSupported = jQuery.support.ajax = !!xhrSupported; + +// Create transport if the browser can provide an xhr +if ( xhrSupported ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var handle, i, + xhr = s.xhr(); + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers["X-Requested-With"] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( err ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + var status, responseHeaders, statusText, responses; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occurred + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + responses = {}; + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + if ( typeof xhr.responseText === "string" ) { + responses.text = xhr.responseText; + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + if ( !s.async ) { + // if we're in sync mode we fire the callback + callback(); + } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been + // retrieved directly we need to fire the callback + setTimeout( callback ); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback( undefined, true ); + } + } + }; + } + }); +} +var fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [function( prop, value ) { + var end, unit, + tween = this.createTween( prop, value ), + parts = rfxnum.exec( value ), + target = tween.cur(), + start = +target || 0, + scale = 1, + maxIterations = 20; + + if ( parts ) { + end = +parts[2]; + unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" && start ) { + // Iteratively approximate from a nonzero starting point + // Prefer the current property, because this process will be trivial if it uses the same units + // Fallback to end or a simple constant + start = jQuery.css( tween.elem, prop, true ) || end || 1; + + do { + // If previous iteration zeroed out, double until we get *something* + // Use a string for doubling factor so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + tween.unit = unit; + tween.start = start; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; + } + return tween; + }] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }); + return ( fxNow = jQuery.now() ); +} + +function createTweens( animation, props ) { + jQuery.each( props, function( prop, value ) { + var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( collection[ index ].call( animation, prop, value ) ) { + + // we're done with this property + return; + } + } + }); +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // if we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // resolve when we played the last frame + // otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + createTweens( animation, props ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +function propFilter( props, specialEasing ) { + var value, name, index, easing, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'index' from above because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +function defaultPrefilter( elem, props, opts ) { + /*jshint validthis:true */ + var prop, index, length, + value, dataShow, toggle, + tween, hooks, oldfire, + anim = this, + style = elem.style, + orig = {}, + handled = [], + hidden = elem.nodeType && isHidden( elem ); + + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // doing this makes sure that the complete handler will be called + // before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( elem, "display" ) === "inline" && + jQuery.css( elem, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { + style.display = "inline-block"; + + } else { + style.zoom = 1; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + if ( !jQuery.support.shrinkWrapBlocks ) { + anim.always(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + } + + + // show/hide pass + for ( index in props ) { + value = props[ index ]; + if ( rfxtypes.exec( value ) ) { + delete props[ index ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + continue; + } + handled.push( index ); + } + } + + length = handled.length; + if ( length ) { + dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + jQuery._removeData( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( index = 0 ; index < length ; index++ ) { + prop = handled[ index ]; + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + } +} + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails + // so, simple values such as "10px" are parsed to Float. + // complex values such as "rotate(1rad)" are returned as is. + result = jQuery.css( tween.elem, tween.prop, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // use step hook for back compat - use cssHook if its there - use .style if its + // available and use plain properties where available + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Remove in 2.0 - this supports IE8's panic based approach +// to setting things on disconnected nodes + +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + doAnimation.finish = function() { + anim.stop( true ); + }; + // Empty animations, or finishing resolves immediately + if ( empty || jQuery._data( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = jQuery._data( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each(function() { + var index, + data = jQuery._data( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // enable finishing flag on private data + data.finish = true; + + // empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.cur && hooks.cur.finish ) { + hooks.cur.finish.call( this ); + } + + // look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // turn off finishing flag + delete data.finish; + }); + } +}); + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + attrs = { height: type }, + i = 0; + + // if we include width, step value is 1 to do all cssExpand values, + // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; + for( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p*Math.PI ) / 2; + } +}; + +jQuery.timers = []; +jQuery.fx = Tween.prototype.init; +jQuery.fx.tick = function() { + var timer, + timers = jQuery.timers, + i = 0; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + if ( timer() && jQuery.timers.push( timer ) ) { + jQuery.fx.start(); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.start = function() { + if ( !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, win, + box = { top: 0, left: 0 }, + elem = this[ 0 ], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== core_strundefined ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + return { + top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ), + left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 ) + }; +}; + +jQuery.offset = { + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, + parentOffset = { top: 0, left: 0 }, + elem = this[ 0 ]; + + // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent + if ( jQuery.css( elem, "position" ) === "fixed" ) { + // we assume that getBoundingClientRect is available when computed position is fixed + offset = elem.getBoundingClientRect(); + } else { + // Get *real* offsetParent + offsetParent = this.offsetParent(); + + // Get correct offsets + offset = this.offset(); + if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) { + parentOffset = offsetParent.offset(); + } + + // Add offsetParent borders + parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ); + } + + // Subtract parent offsets and element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true) + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.documentElement; + while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || document.documentElement; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + win.document.documentElement[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return jQuery.access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest + // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); +// Limit scope pollution from any deprecated API +// (function() { + +// })(); +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +})( window ); |
