From 5e5f0be59f8ffac7146e9385a4c0f960b979a513 Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 12 Feb 2014 16:15:05 +0530 Subject: [PATCH 01/68] Bring back command number to command output header --- web/clira/controllers.js | 15 ++++++++------- web/clira/index.html | 4 +++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/web/clira/controllers.js b/web/clira/controllers.js index f07792e..9c0c048 100644 --- a/web/clira/controllers.js +++ b/web/clira/controllers.js @@ -101,9 +101,17 @@ Clira.CommandInputController = Em.ObjectController.extend({ parseErrors = null; } + if ($.clira.commandCount) { + this.set('commandNumber', ++$.clira.commandCount); + } else { + $.clira.commandCount = 1; + this.set('commandNumber', 1); + } + var content = { contentTemplate: templateName, command: finalCommand, + commandNumber: this.get('commandNumber'), completed: false, messages: parseErrors, output: output, @@ -120,13 +128,6 @@ Clira.CommandInputController = Em.ObjectController.extend({ this.get('controllers.outputs') .unshiftObject(Clira.OutputContainerController.create(content)); - if ($.clira.commandCount) { - this.set('commandNumber', ++$.clira.commandCount); - } else { - $.clira.commandCount = 1; - this.set('commandNumber', 1); - } - // Reset command input field this.set('command', ''); }, diff --git a/web/clira/index.html b/web/clira/index.html index 9c1922e..8bafecb 100644 --- a/web/clira/index.html +++ b/web/clira/index.html @@ -61,7 +61,9 @@ {{view Clira.IconView iconClass="ui-icon-closethick" onClick="close"}} {{view Clira.IconView iconClass="ui-icon-minusthick,ui-icon-plusthick" onClick="collapse"}} {{controller.command}} - +
+ ({{controller.commandNumber}}) +
From 17dc43a178d8ce6009020fbaea27708eb52113b4 Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 26 Feb 2014 15:48:22 +0530 Subject: [PATCH 12/68] Display welcome message on start up --- web/clira/clira.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/clira/clira.js b/web/clira/clira.js index c8a8300..8fb47a7 100644 --- a/web/clira/clira.js +++ b/web/clira/clira.js @@ -141,7 +141,11 @@ jQuery(function ($) { content.poss = poss; if (content.commandNumber == undefined) { - content.commandNumber = $.clira.commandCount ? ++$.clira.commandCount : 1; + if ($.clira.commandCount) { + content.commandNumber = ++$.clira.commandCount; + else { + content.commandNumber = 1; + } } Clira.__container__.lookup('controller:outputs') From 8b687581c9bb7c5fb705b332ea3398ba809cee27 Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 26 Feb 2014 16:11:31 +0530 Subject: [PATCH 13/68] Display welcome message on start up --- web/clira/clira.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/clira/clira.js b/web/clira/clira.js index 8fb47a7..b152684 100644 --- a/web/clira/clira.js +++ b/web/clira/clira.js @@ -143,7 +143,7 @@ jQuery(function ($) { if (content.commandNumber == undefined) { if ($.clira.commandCount) { content.commandNumber = ++$.clira.commandCount; - else { + } else { content.commandNumber = 1; } } From cd227a21e06035f3a0f9a7555a5ced544a304c14 Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 26 Feb 2014 19:30:37 +0530 Subject: [PATCH 14/68] Added new command 'show debug messages' to view debug logs in an output container replacing sticky window --- web/clira/app.js | 9 --------- web/clira/index.html | 11 ----------- web/clira/system/debug.js | 17 +++++++++++++---- web/jquery/jquery.dbgpr.js | 9 +++++---- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/web/clira/app.js b/web/clira/app.js index a13a6da..104745a 100644 --- a/web/clira/app.js +++ b/web/clira/app.js @@ -132,13 +132,4 @@ jQuery(function ($) { }); $.clira.prefsInit(); - - // Icons on debug container - $.clira.decorateIcons($("#debug-container")); - - // Hide debug container when not debugging - if (localStorage['debug'] - && JSON.parse(localStorage['debug']) == true) { - $("#debug-container").css({ display: "inline" }); - } }); diff --git a/web/clira/index.html b/web/clira/index.html index ebfc110..bc92d05 100644 --- a/web/clira/index.html +++ b/web/clira/index.html @@ -156,17 +156,6 @@ {{/each}} -
-
- - - - -
Debug Log:
-
-
 
-
- diff --git a/web/clira/system/debug.js b/web/clira/system/debug.js index b3d7960..2e88d51 100644 --- a/web/clira/system/debug.js +++ b/web/clira/system/debug.js @@ -18,7 +18,6 @@ jQuery(function($) { help: "Log debug messages", execute: function (view, cmd, parse, poss) { localStorage['debug'] = true; - $("#debug-container").css({ display: "inline" }); view.get('controller').set('output', 'Enabled debugging'); } }, @@ -27,8 +26,6 @@ jQuery(function($) { help: "Disable logging of debug messages", execute: function (view, cmd, parse, poss) { localStorage['debug'] = false; - $("#debug-log").empty(); - $("#debug-container").css({ display: "none" }); view.get('controller').set('output', 'Disabled debugging'); } }, @@ -45,9 +42,21 @@ jQuery(function($) { help: "Enable/resume logging of debug messages", execute: function (view, cmd, parse, poss) { localStorage['debug'] = true; - $("#debug-container").css({ display: "inline" }); view.get('controller').set('output', 'Resumed debugging'); } + }, + { + command: "show debug messages", + help: "Display debug information", + execute: function (view, cmd, parse, poss) { + // Create and attach debug-log container to this + // output-container to which we append log messages + var g = document.createElement('div'); + g.setAttribute('id', 'debug-log'); + view.$().find('.output-content')[0].appendChild(g); + localStorage['debug-log'] = view.elementId + + ' .output-content #debug-log'; + } } ] }); diff --git a/web/jquery/jquery.dbgpr.js b/web/jquery/jquery.dbgpr.js index 94b2113..48ec94d 100644 --- a/web/jquery/jquery.dbgpr.js +++ b/web/jquery/jquery.dbgpr.js @@ -10,12 +10,13 @@ jQuery(function ($) { * dbgpr() is our old-and-trusted debug print function */ $.dbgpr = function () { - var debug = localStorage['debug'] ? + var debug = localStorage['debug'] && localStorage['debug-log'] ? JSON.parse(localStorage['debug']) : true; /* The actual work is pretty trivial */ - if (debug == null || debug) - $('#debug-log').append(Array.prototype.slice - .call(arguments).join(" ") + "\n"); + if (debug == null || debug) { + $('#' + localStorage['debug-log']).append(Array.prototype.slice + .call(arguments).join(" ") + "
"); + } } }); From 1efd1ce68d73c669ef7fa081d686ea2e16c33472 Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 26 Feb 2014 21:09:29 +0530 Subject: [PATCH 15/68] Update ember-restless to 0.5.0 that includes LSAdapter --- .../ember-restless/ember-restless+extras.js | 1457 +++++++++++------ web/external/ember-restless/ember-restless.js | 1344 ++++++++++----- 2 files changed, 1911 insertions(+), 890 deletions(-) diff --git a/web/external/ember-restless/ember-restless+extras.js b/web/external/ember-restless/ember-restless+extras.js index ee0d917..06d2061 100644 --- a/web/external/ember-restless/ember-restless+extras.js +++ b/web/external/ember-restless/ember-restless+extras.js @@ -2,8 +2,8 @@ * ember-restless * A lightweight data persistence library for Ember.js * - * version: 0.4.0 - * last modifed: 2013-09-16 + * version: 0.5.0 + * last modifed: 2014-02-19 * * Garth Poitras * Copyright (c) 2013 Endless, Inc. @@ -13,46 +13,74 @@ "use strict"; +/** + @module ember-restless + */ var get = Ember.get, set = Ember.set, none = Ember.isNone, empty = Ember.isEmpty, + exports = Ember.exports || this, RESTless; -if (RESTless === undefined) { +if ('undefined' === typeof RESTless) { /** - * Create RESTless as an Ember Namespace. - * - * @class RESTless - * @static + All Ember RESTless functionality is defined inside of this namespace. + @class RESTless + @static */ - RESTless = Ember.Namespace.create(); + RESTless = Ember.Namespace.create({ + VERSION: '0.5.0' + }); - /** - * Expose RESTless to the global window namespace. - * Create shortcut alias 'RL'. + /* + A shortcut alias to the RESTless namespace. + Similar to `Ember` and `Em`. + Expose to global namespace. */ - if (window !== undefined) { - window.RL = window.RESTless = RESTless; + exports.RL = exports.RESTless = RESTless; + + if (Ember.libraries) { + Ember.libraries.register('Ember RESTless', RESTless.VERSION); } } /** - * Attributes - * Model property definitions - */ - -// Standard attribute + Defines an attribute on a Model. + Supports types: 'string', 'number', 'boolean', 'date'. + + @method attr + @for RESTless + @param {String} type the attribute type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.attr = function(type, opts) { var meta = Ember.merge({ type: type, isAttribute: true }, opts); return makeComputedAttribute(meta); }; -// belongsTo: One-to-one relationships +/** + Defines a one-to-one relationship attribute on a Model. + + @method belongsTo + @for RESTless + @param {String} type the belongsTo Class type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.belongsTo = function(type, opts) { var meta = Ember.merge({ type: type, isRelationship: true, belongsTo: true }, opts); return makeComputedAttribute(meta); }; -// hasMany: One-to-many & many-to-many relationships +/** + Defines a one-to-many relationship attribute on a Model. + + @method hasMany + @for RESTless + @param {String} type the hasMany Class type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.hasMany = function(type, opts) { var defaultArray = function() { return RESTless.RecordArray.createWithContent(); @@ -89,58 +117,118 @@ function makeComputedAttribute(meta) { }).property('_data').meta(meta); } -/* - * Serializer - * Base serializer to be subclassed. - * Handles transforming data before saving to persistence layer - * and transforming data into Models when retrieving - */ +/** + Serializers handle transforming data to and from raw data and Models. + This is a base class to be subclassed. + + @class Serializer + @namespace RESTless + @extends Ember.Object +*/ RESTless.Serializer = Ember.Object.extend({ - /* - * dataType: i.e. json, jsonp, xml, html - */ + /** + Type of data to serialize. + @property dataType + @type String + @example json, jsonp, xml, html + */ dataType: null, - /* - * contentType: additional content type headers - */ + /** + Additional content type headers when transmitting data. + @property dataType + @type String + @optional + */ contentType: null, - /* - * Common serializer methods to be implemented in a subclass - */ - deserialize: Ember.K, + /** + Transforms raw data into model. Abstract - implement in subclass. + @method deserialize + */ + deserialize: Ember.K, + /** + Transforms raw data property into model property. Abstract - implement in subclass. + @method deserializeProperty + */ deserializeProperty: Ember.K, - deserializeMany: Ember.K, - serialize: Ember.K, - serializeProperty: Ember.K, - serializeMany: Ember.K, + /** + Transforms array of raw data into record array. Abstract - implement in subclass. + @method deserializeMany + */ + deserializeMany: Ember.K, + /** + Transforms model into raw data. Abstract - implement in subclass. + @method serialize + */ + serialize: Ember.K, + /** + Transforms model property into raw data property. Abstract - implement in subclass. + @method serializeProperty + */ + serializeProperty: Ember.K, + /** + Transforms a record array into raw data array. Abstract - implement in subclass. + @method serializeMany + */ + serializeMany: Ember.K, + /** + To register a custom attribute transform. Abstract - implement in subclass. + @method registerTransform + @optional + */ + registerTransform: Ember.K, - /* - * prepareData: (optional override) preps data before persisting - */ + /** + Optional override to prep data before persisting. + @method prepareData + @return Object + @optional + */ prepareData: function(data) { return data; }, - /* - * parseError: (optional override) deserialize error messages - */ + /** + Optional override to deserialize error messages. + @method parseError + @return Object + @optional + */ parseError: function(error) { return error; } }); -/* - * JSONSerializer - * Serializes and deserializes data to and from json - */ +/** + Handles transforming json data to Models and Models to json data. + + @class JSONSerializer + @namespace RESTless + @extends RESTless.Serializer +*/ RESTless.JSONSerializer = RESTless.Serializer.extend({ + /** + Type of data to serialize. + @property dataType + @type String + @default 'json' + */ dataType: 'json', + /** + Additional content type headers when transmitting data. + @property contentType + @type String + @default 'application/json; charset=utf-8' + */ contentType: 'application/json; charset=utf-8', - /* - * deserialize: translates json object into a model object - */ + /** + Transforms json object into model + @method deserialize + @param {RESTless.Model} resource model resource + @param {Object} data json data + @return {RESTless.Model} + */ deserialize: function(resource, data) { if(!data) { return resource; } @@ -166,15 +254,20 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ this.deserializeProperty(resource, prop, data[prop]); } } + resource.setProperties({ isLoaded: true, isDirty: false }); Ember.endPropertyChanges(resource); return resource; }, - /* - * deserializeProperty: sets model object properties from json - */ + /** + Transforms json key/value into model property + @method deserializeProperty + @param {RESTless.Model} resource model resource + @param {Object} prop json data key + @param {Object} value json data value + */ deserializeProperty: function(resource, prop, value) { - var attrName = this.attributeNameForKey(resource, prop), + var attrName = this.attributeNameForKey(resource.constructor, prop), fields = get(resource.constructor, 'fields'), field = fields.get(attrName), type, klass; @@ -191,8 +284,7 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ } // If property is a belongsTo relationship, deserialze that model else if (field.belongsTo && klass && value) { - var belongsToModel = klass.create({ isNew: false }).deserialize(value); - belongsToModel.onLoaded(); + var belongsToModel = klass.create({ isNew: false, isLoaded: true }).deserialize(value); resource.set(attrName, belongsToModel); } else { @@ -204,55 +296,62 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ } }, - /* - * deserializeMany: deserializes an array of json objects - */ + /** + Transforms json array into a record array + @method deserializeMany + @param {RESTless.RecordArray} recordArray RecordArray + @param {String} type records class name + @param {Object} data json data + @return {RESTless.RecordArray} + */ deserializeMany: function(recordArray, type, data) { if(!data) { return recordArray; } - var klass = get(Ember.lookup, type), meta, keyPlural, len, i, item; + var klass = get(Ember.lookup, type), + arrayData = this._arrayDataForType(type, data), + meta, i, len, item, content; - // extract any meta info - meta = this.extractMeta(data); - if(meta) { recordArray.set('meta', meta); } - - // Check for wrapped array by resource name: { posts: [...] } - // This is the default from ActiveRecord on direct finds - if(!Ember.isArray(data)) { - keyPlural = this._keyPluralForResourceType(type); - if(data[keyPlural]) { - data = data[keyPlural]; - } else { - return recordArray; - } - } + if(!arrayData) { return recordArray; } if(recordArray) { + recordArray.set('isLoaded', false); recordArray.clear(); } else { recordArray = RESTless.RecordArray.createWithContent(); } - len = data.length; + len = arrayData.length; if(len) { - Ember.beginPropertyChanges(recordArray); + content = []; for(i=0; i + App.Adapter.map('App.Post', { primaryKey: 'slug' }); + App.Adapter.map('App.Person', { lastName: { key: 'lastNameOfPerson' } }); + */ map: function(modelKey, config) { var modelMap = this.get('configurations.models'), modelConfig = modelMap.get(modelKey), @@ -526,124 +731,205 @@ RESTless.Adapter = Ember.Object.extend({ return this; }, - /* - * pluralize: helper to pluralize model resource names. - * Checks custom configs or simply appends a 's' - */ + /** + Helper to pluralize a model resource names. + Checks custom configs or simply appends a 's'. + @method pluralize + @param {String} resourceName Name of model class + @return {String} plural name + */ pluralize: function(resourceName) { var plurals = this.get('configurations.plurals'); return (plurals && plurals[resourceName]) || resourceName + 's'; } }); -/* - * RESTAdapter - * Builds REST urls to resources - * Builds and handles remote ajax requests - */ +/** + The REST Adapter handles sending and fetching data to and from a REST API. + + @class RESTAdapter + @namespace RESTless + @extends RESTless.Adapter +*/ RESTless.RESTAdapter = RESTless.Adapter.extend({ - /* - * serializer: default to a JSON serializer + /** + Serializer used to transform data. + @property serializer + @type RESTless.Serializer + @default RESTless.JSONSerializer */ serializer: RESTless.JSONSerializer.create(), - /* - * url: base url of backend REST service - * example: 'https://api.example.com' + /** + Host url of the REST API if on a different domain than the app. + @property host + @type String + @optional + @example 'http://api.example.com' + */ + host: Ember.computed.oneWay('url'), + /** + Deprecated. + @property url + @type String + @deprecated Use: `host` */ url: null, - /* - * namespace: endpoint path - * example: 'api/v1' + + /** + API namespace endpoint path + @property namespace + @type String + @optional + @example 'api/v1' */ namespace: null, - /* - * useContentTypeExtension: forces content type extensions on resource requests - * i.e. /posts.json vs /posts | /posts/115.json vs /posts/115 - * Useful for conforming to 3rd party apis - * or returning correct content-type headers with Rails caching + + /** + If an API requires certain headers to be transmitted, e.g. an api key, + you can add a hash of headers to be sent on each request. + @property headers + @type Object + @optional + @example '{ "X-API-KEY" : "abc1234" }' + */ + headers: null, + /** + If an API requires paramters to be set on every request, + e.g. an api key, you can add a hash of defaults. + @property defaultData + @type Object + @optional + @example '{ api_key: "abc1234" }' + */ + defaultData: null, + + /** + Adds content type extensions to requests. + @property useContentTypeExtension + @type Boolean + @default false + @example + When `true` will make requests `/posts.json` instead of `/posts` or `/posts/115.json` instead of `/posts/115` */ useContentTypeExtension: false, - /* - * rootPath: computed path based on url and namespace + /** + Computed path based on host and namespace. + @property rootPath + @type String + @final */ rootPath: Ember.computed(function() { var a = document.createElement('a'), - url = this.get('url'), + host = this.get('host'), ns = this.get('namespace'), rootReset = ns && ns.charAt(0) === '/'; - a.href = url ? url : ''; + a.href = host ? host : '/'; if(ns) { a.pathname = rootReset ? ns : (a.pathname + ns); } return a.href.replace(/\/+$/, ''); - }).property('url', 'namespace'), + }).property('host', 'namespace'), - /* - * resourcePath: helper method creates a valid REST path to a resource - * App.Post => 'posts', App.PostGroup => 'post_groups' + /** + Helper method creates a valid REST path to a resource + @method resourcePath + @param {String} resourceName Type of Model + @return {String} the resource path + @example App.Post => 'posts', App.PostGroup => 'post_groups' */ resourcePath: function(resourceName) { return this.pluralize(Ember.String.decamelize(resourceName)); }, - /* - * request: creates and executes an ajax request wrapped in a promise + /** + Creates and executes an ajax request wrapped in a promise. + @method request + @param {RESTless.Model} model model to use to build the request + @param {Object} [params] Additional ajax params + @param {Object} [key] optional resource primary key value + @return {Ember.RSVP.Promise} */ request: function(model, params, key) { - var adapter = this, serializer = this.serializer; + var adapter = this, + ajaxParams = this.prepareParams(params); + ajaxParams.url = this.buildUrl(model, key); return new Ember.RSVP.Promise(function(resolve, reject) { - params = params || {}; - params.url = adapter.buildUrl(model, key); - params.dataType = serializer.dataType; - params.contentType = serializer.contentType; - - if(params.data && params.type !== 'GET') { - params.data = serializer.prepareData(params.data); - } - - params.success = function(data, textStatus, jqXHR) { + ajaxParams.success = function(data) { Ember.run(null, resolve, data); }; - params.error = function(jqXHR, textStatus, errorThrown) { + ajaxParams.error = function(jqXHR, textStatus, errorThrown) { var errors = adapter.parseAjaxErrors(jqXHR, textStatus, errorThrown); Ember.run(null, reject, errors); }; - var ajax = Ember.$.ajax(params); - + var ajax = Ember.$.ajax(ajaxParams); // (private) store current ajax request on the model. - model.set('currentRequest', ajax); + model.currentRequest = ajax; }); }, - /* - * buildUrl (private): constructs request url and dynamically adds the a resource key if specified + /** + Builds ajax request parameters + @method prepareParams + @param {Object} [params] base ajax params + @return {Object} + @private + */ + prepareParams: function(params) { + var serializer = this.serializer, + headers = this.get('headers'), + defaultData = this.get('defaultData'); + params = params || {}; + params.dataType = serializer.dataType; + params.contentType = serializer.contentType; + if(headers) { + params.headers = headers; + } + if(defaultData) { + params.data = $.extend({}, defaultData, params.data); + } + if(params.data && params.type !== 'GET') { + params.data = serializer.prepareData(params.data); + } + return params; + }, + + /** + Constructs request url and dynamically adds the resource key if specified + @method buildUrl + @private */ buildUrl: function(model, key) { var resourcePath = this.resourcePath(get(model.constructor, 'resourceName')), primaryKey = get(model.constructor, 'primaryKey'), urlParts = [this.get('rootPath'), resourcePath], - dataType = this.get('serializer.dataType'), url; + dataType, url; if(key) { urlParts.push(key); } else if(model.get(primaryKey)) { urlParts.push(model.get(primaryKey)); } - url = urlParts.join('/'); - if(this.get('useContentTypeExtension') && dataType) { - url += '.' + dataType; + + if(this.useContentTypeExtension) { + dataType = this.serializer.dataType; + if(dataType) { + url += '.' + dataType; + } } return url; }, - /* - * saveRecord: POSTs a new record, or PUTs an updated record to REST service + /** + Saves a record. POSTs a new record, or PUTs an updated record to REST API + @method saveRecord + @param {RESTless.Model} record record to be saved + @return {Ember.RSVP.Promise} */ saveRecord: function(record) { var isNew = record.get('isNew'), method, ajaxPromise; @@ -672,6 +958,12 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Deletes a record from REST API using DELETE + @method deleteRecord + @param {RESTless.Model} record record to be deleted + @return {Ember.RSVP.Promise} + */ deleteRecord: function(record) { var ajaxPromise = this.request(record, { type: 'DELETE', data: record.serialize() }); @@ -686,6 +978,12 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Reloads a record from REST API + @method reloadRecord + @param {RESTless.Model} record record to be reloaded + @return {Ember.RSVP.Promise} + */ reloadRecord: function(record) { var klass = record.constructor, primaryKey = get(klass, 'primaryKey'), @@ -710,10 +1008,23 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Finds all records of specified class using GET + @method findAll + @param {RESTless.Model} klass model type to find + @return {RESTless.RecordArray} + */ findAll: function(klass) { return this.findQuery(klass); }, + /** + Finds records with specified query params using GET + @method findQuery + @param {RESTless.Model} klass model type to find + @param {Object} queryParams hash of query params + @return {RESTless.RecordArray} + */ findQuery: function(klass, queryParams) { var type = klass.toString(), resourceInstance = klass.create({ isNew: false }), @@ -730,6 +1041,14 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return result; }, + /** + Finds record with specified primary key using GET + @method findByKey + @param {RESTless.Model} klass model type to find + @param {Number|String} key primary key value + @param {Object} [queryParams] hash of additional query params + @return {RESTless.Model} + */ findByKey: function(klass, key, queryParams) { var result = klass.create({ isNew: false }), ajaxPromise = this.request(result, { type: 'GET', data: queryParams }, key); @@ -744,29 +1063,20 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return result; }, - /* - * fetch: wraps find method in a promise for async find support - * Overridden to add currentRequest - */ - fetch: function(klass, params) { - var adapter = this, find, promise; - promise = new Ember.RSVP.Promise(function(resolve, reject) { - find = adapter.find(klass, params); - find.one('didLoad', function(model) { - resolve(model); - }); - find.one('becameError', function(error) { - reject(error); - }); - }); - // private: store the ajax request for aborting, etc. - promise._currentRequest = find.get('currentRequest'); - return promise; + /** + Registers custom attribute transforms. + Fowards creation to serializer. + @method registerTransform + */ + registerTransform: function(type, transform) { + this.get('serializer').registerTransform(type, transform); }, - /* - * parseAjaxErrors: builds a robust error object using the serializer and xhr data - */ + /** + Builds a robust error object using the serializer and xhr data + @method parseAjaxErrors + @private + */ parseAjaxErrors: function(jqXHR, textStatus, errorThrown) { // use serializer to parse error messages from server var errors = this.get('serializer').parseError(jqXHR.responseText) || {}; @@ -776,29 +1086,29 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ errors.textStatus = textStatus; errors.errorThrown = errorThrown; return errors; - }, - - /* - * registerTransform: fowards custom tranform creation to serializer - */ - registerTransform: function(type, transform) { - this.get('serializer').registerTransform(type, transform); } }); /** - * Client - * You can define a custom Client on your app like you would DS.Store in ember-data. - * The client will be automatically detected and set from your App namespace. - * Setting a Client is optional and will automatically use a base Client. - * - * @class Client - * @namespace RESTless - * @extends Ember.Object - */ + The Client is the top level store, housing the default adapter and configurations. + The client will be automatically detected and set from your App namespace. + Setting a client is optional and will automatically use a base client. + + @class Client + @namespace RESTless + @extends Ember.Object +*/ RESTless.Client = Ember.Object.extend({ + /** + The default adapter for all models. + @property adapter + @type RESTless.Adapter + */ adapter: RESTless.RESTAdapter.create(), - // Private shortcut aliases: + /** + Shortcut alias to model configurations + @private + */ _modelConfigs: Ember.computed.alias('adapter.configurations.models') }); @@ -806,15 +1116,8 @@ Ember.onLoad('Ember.Application', function(Application) { Application.initializer({ name: 'Client', initialize: function(container, application) { - // On app initialize, if custom Client is present, - // set that as the default client - if(application.Client) { - RESTless.set('client', application.Client); - } else { - // Set a default client - RESTless.set('client', RESTless.Client.create()); - } - // Add an observer so you can set a client at a later date + var client = application.Client ? application.Client : RESTless.Client.create(); + RESTless.set('client', client); application.addObserver('Client', this, function() { RESTless.set('client', this.Client); }); @@ -822,52 +1125,83 @@ Ember.onLoad('Ember.Application', function(Application) { }); }); -/* - * State - * Mixin for managing model lifecycle state - */ +/** + The State Mixin is responsible for keeping state and + handling state events for Models. + + @class State + @namespace RESTless + @uses Ember.Evented +*/ RESTless.State = Ember.Mixin.create( Ember.Evented, { - /* - * isNew: model has not yet been saved. - */ + /** + Model has not yet been saved; not yet assigned a primary key. + @property isNew + @type {Boolean} + */ isNew: true, - /* - * isLoaded: model has been retrieved - */ + /** + Model has been retrieved and properties set. + @property isLoaded + @type {Boolean} + */ isLoaded: false, - /* - * isDirty: model has changes that have not yet been saved - */ + /** + Model has changes that have not yet been saved. + @property isDirty + @type {Boolean} + */ isDirty: false, - /* - * isSaving: model is in the process of saving - */ + /** + Model model is in the process of saving. + @property isSaving + @type {Boolean} + */ isSaving: false, - /* - * isError: model has been marked as invalid after response from adapter - */ + /** + Model model is in error state. + @property isError + @type {Boolean} + */ isError: false, - /* - * _isReady (private) - * Flag for deferring dirty state when setting initial values on create() or load() - */ - _isReady: false, - /* - * errors: error data returned from adapter - */ + + /** + Hash of current errors on model. + @property errors + @type Object + */ errors: null, - /* - * State event hooks - */ - didCreate: Ember.K, - didUpdate: Ember.K, - didLoad: Ember.K, - didDelete: Ember.K, - becameError: Ember.K, + /** + Fired when the record is created. + @event didCreate + */ + didCreate: Ember.K, + /** + Fired when the record is updated. + @event didUpdate + */ + didUpdate: Ember.K, + /** + Fired when the record is enters the loaded state. + @event didLoad + */ + didLoad: Ember.K, + /** + Fired when the record is deleted. + @event didDelete + */ + didDelete: Ember.K, + /** + Fired when the record enters the error state. + @event becameError + */ + becameError: Ember.K, - /* - * Internal state change handlers, called by adapter + /** + Updates state and triggers events upon saving. + @method onSaved + @param {Boolean} wasNew was a new model prior to saving. */ onSaved: function(wasNew) { this.setProperties({ @@ -881,6 +1215,10 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('didLoad', this); }, + /** + Updates state and triggers events upon deletion. + @method onDeleted + */ onDeleted: function() { this._triggerEvent('didDelete', this); Ember.run.next(this, function() { @@ -888,8 +1226,14 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { }); }, + /** + Updates state and triggers events upon loading. + @method onLoaded + */ onLoaded: function() { this.setProperties({ + isDirty: false, + isSaving: false, isLoaded: true, isError: false, errors: null @@ -897,6 +1241,10 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('didLoad', this); }, + /** + Updates state and triggers events upon an error. + @method onError + */ onError: function(errors) { this.setProperties({ isSaving: false, @@ -906,16 +1254,21 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('becameError', errors); }, - /* - * clearErrors: (helper) reset isError flag, clear error messages + /** + Clears errors and resets error state + @method clearErrors + @returns {Object} */ clearErrors: function() { this.setProperties({ isError: false, errors: null }); return this; }, - /* - * copyState: copies the current state to a cloned object + /** + Copies the current state to a cloned object + @method copyState + @param {Object} clone the cloned object + @returns {Object} the cloned object with copied state */ copyState: function(clone) { var mi = RESTless.State.mixins, @@ -930,6 +1283,19 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { return clone; }, + /** + Flag for deferring dirty state when setting initial values on create() or load() + @property _isReady + @type {Boolean} + @private + */ + _isReady: false, + + /** + Helper function to trigger events on models and to any listeners. + @method _triggerEvent + @private + */ _triggerEvent: function(event, data) { Ember.run(this, function() { Ember.tryInvoke(this, event, [data]); @@ -939,27 +1305,24 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { }); /** - * Model - * Base model class for all records - * - * @class Model - * @namespace RESTless - * @extends Ember.Object - * @constructor - */ + The base model class for all RESTless objects. + + @class Model + @namespace RESTless + @extends Ember.Object + @uses RESTless.State + @uses Ember.Copyable +*/ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { /** - * id: unique id number, default primary id - * - * @property {RESTless.attr} + A unique id number for the record. `id` is the default primary key. + @property id */ id: RESTless.attr('number'), /** - * _data: Stores raw model data. Don't use directly; use declared model attributes. - * - * @private - * @property {Object} + Stores raw model data. Don't use directly; use declared model attributes. + @private */ __data: null, _data: Ember.computed(function() { @@ -968,7 +1331,8 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }), /** - * didDefineProperty: Hook to add observers for each attribute/relationship for 'isDirty' functionality + Hook to add observers for each attribute/relationship for 'isDirty' functionality + @protected */ didDefineProperty: function(proto, key, value) { if (value instanceof Ember.Descriptor) { @@ -982,9 +1346,9 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }, /** - * _onPropertyChange: called when any property of the model changes - * If the model has been loaded, or is new, isDirty flag is set to true. - * @private + _onPropertyChange: called when any property of the model changes + If the model has been loaded, or is new, isDirty flag is set to true. + @private */ _onPropertyChange: function(key) { var isNew = this.get('isNew'); @@ -1000,9 +1364,9 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { } }, /** - * _onRelationshipChange: called when a relationship property's isDirty state changes - * Forwards a _onPropertyChange event for the parent object - * @private + Called when a relationship property's isDirty state changes. + Forwards a _onPropertyChange event for the parent object. + @private */ _onRelationshipChange: function(sender, key) { if(sender.get(key)) { // if isDirty @@ -1011,8 +1375,11 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }, /** - * copy: creates a copy of the object. Implements Ember.Copyable protocol - * http://emberjs.com/api/classes/Ember.Copyable.html#method_copy + Creates a clone of the model. Implements Ember.Copyable protocol + + @method copy + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver */ copy: function(deep) { var clone = this.constructor.create(), @@ -1029,73 +1396,120 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { return clone; }, - /* - * copyWithState: creates a copy of the object along with the RESTless.State properties + + /** + Creates a clone copy of the model along with it's current State. + @method copyWithState + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver */ copyWithState: function(deep) { return this.copyState(this.copy(deep)); }, - /* - * create/update/delete methods - * Forward to the current adapter to perform operations on persistance layer - */ + /** + Saves the record using the model's adapter. + @method saveRecord + @chainable + */ saveRecord: function() { return get(this.constructor, 'adapter').saveRecord(this); }, + /** + Deletes the record using the model's adapter. + @method deleteRecord + @chainable + */ deleteRecord: function() { return get(this.constructor, 'adapter').deleteRecord(this); }, + /** + Reloads the record using the model's adapter. + @method reloadRecord + @chainable + */ reloadRecord: function() { return get(this.constructor, 'adapter').reloadRecord(this); }, - /* - * serialization methods: Transforms model to and from its data representation. - * Forward to the current serializer to perform appropriate parsing - */ + /** + Serializes the record into its data representaion. + @method serialize + @param {Object} options hash of serialization options + @chainable + */ serialize: function(options) { return RESTless.get('client.adapter.serializer').serialize(this, options); }, + /** + Deserializes raw data into Model properties + @method deserialize + @param {Object} data raw data to deserialize + @chainable + */ deserialize: function(data) { return RESTless.get('client.adapter.serializer').deserialize(this, data); }, + /** + Serializes a Model property into its data representaion. + @method serializeProperty + @param {String} prop property key + @chainable + */ serializeProperty: function(prop) { return RESTless.get('client.adapter.serializer').serializeProperty(this, prop); }, + /** + Deserializes raw data property into Model property + @method deserializeProperty + @param {String} prop property key + @param value property value + @chainable + */ deserializeProperty: function(prop, value) { return RESTless.get('client.adapter.serializer').deserializeProperty(this, prop, value); } }); -/* - * RESTless.Model (static) - * Class level properties and methods - */ +/** + Static properties and methods for the Model Class. + + @class Model + @namespace RESTless +*/ RESTless.Model.reopenClass({ - /* - * create: standard super class create, then marks _isReady state flag + /** + Extends super class `create` and marks _isReady state. + @method create + @return RESTless.Model */ create: function() { var instance = this._super.apply(this, arguments); instance.set('_isReady', true); return instance; }, - /* - * createRecord: alias to create. Ease transition to/from ember-data + /** + Alias to `create`. Eases transition to/from ember-data + @deprecated Use `create` + @method createRecord + @return RESTless.Model */ createRecord: Ember.aliasMethod('create'), - /* - * adapter: hook to override which adapter instance to use per model + /** + The adapter for the Model. Provides a hook for overriding. + @property adapter + @type RESTless.Adapter */ adapter: Ember.computed(function() { return get(RESTless, 'client.adapter'); }).property('RESTless.client.adapter'), - /* - * primaryKey: property name for the primary key. - * Configurable. Defaults to 'id'. + /** + The property name for the primary key + @property primaryKey + @type String + @default 'id' */ primaryKey: Ember.computed(function() { var className = this.toString(), @@ -1106,17 +1520,34 @@ RESTless.Model.reopenClass({ return 'id'; }).property('RESTless.client._modelConfigs'), - /* - * resourceName: helper to extract name of model subclass - * App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' + /** + The name of the resource, derived from the class name. + App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' + + @property resourceName + @type String */ resourceName: Ember.computed(function() { var classNameParts = this.toString().split('.'); return classNameParts[classNameParts.length-1]; }), + /** + The plural name of the resource, derived from the class name. + App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' - /* - * fields: meta information for all attributes and relationships + @property resourceNamePlural + @type String + */ + resourceNamePlural: Ember.computed(function() { + var resourceName = get(this, 'resourceName'), + adapter = get(this, 'adapter'); + return adapter.pluralize(Ember.String.decamelize(resourceName)); + }), + + /** + Meta information for all attributes and relationships + @property fields + @type Ember.Map */ fields: Ember.computed(function() { var map = Ember.Map.create(); @@ -1128,42 +1559,76 @@ RESTless.Model.reopenClass({ return map; }), - /* - * find methods: retrieve model(s) with specified params - * Forwards to the current adapter to retrieve from the appropriate data layer + /** + Find resources using the adapter. + This method can handle all find types: `findAll`, `findQuery`, `findByKey` + @method find + @param {Object} params + @return Object */ find: function(params) { return get(this, 'adapter').find(this, params); }, + /** + Finds resources using the adapter, and returns a promise. + @method fetch + @param {Object} params hash of query params + @return Ember.RSVP.Promise + */ fetch: function(params) { return get(this, 'adapter').fetch(this, params); }, + /** + Finds all resources of this type using the adapter. + @method findAll + @return Object + */ findAll: function() { return get(this, 'adapter').findAll(this); }, + /** + Find resources with query using the adapter. + @method findQuery + @param {Object} params hash of query params + @return Object + */ findQuery: function(params) { return get(this, 'adapter').findQuery(this, params); }, + /** + Find resource with specified primary key using the adapter. + @method findByKey + @param {Number|String} key + @param {Object} params any additional params + @return Object + */ findByKey: function(key, params) { return get(this, 'adapter').findByKey(this, key, params); }, - /* - * findById: alias to findByKey method - * Keeps api inline with ember-data. - * A model's primary key can be customized so findById is not always semantically correct. + /** + Find resource with specified id using the adapter. + Keeps API similar to ember-data. + @method findById + @deprecated Use `findByKey` */ findById: Ember.aliasMethod('findByKey'), - /* - * load: Create model directly from data representation. + /** + Create model directly from data representation. + @method load + @param {Object} data raw data to load + @return RESTless.Model */ load: function(data) { var model = this.create().set('_isReady', false).deserialize(data).set('_isReady', true); model.onLoaded(); return model; }, - /* - * loadMany: Create collection of records directly from data representation. + /** + Create collection of records directly from data representation.. + @method loadMany + @param {Object} data raw data to load + @return RESTless.RecordArray */ loadMany: function(data) { var array = RESTless.RecordArray.createWithContent().deserializeMany(this.toString(), data); @@ -1172,16 +1637,15 @@ RESTless.Model.reopenClass({ } }); -/* - * ReadOnlyModel - * Subclass for models that are read-only. - * Removes property change observers and write methods. - * Helps improve performance when write functionality is not needed. - */ +/** + A read-only model. Removes property change observers and write methods. + Helps improve performance when write functionality is not needed. + + @class ReadOnlyModel + @namespace RESTless + @extends RESTless.Model +*/ RESTless.ReadOnlyModel = RESTless.Model.extend({ - /* - * Remove functionality associated with writing data and keeping state - */ serialize: null, saveRecord: null, deleteRecord: null, @@ -1189,34 +1653,50 @@ RESTless.ReadOnlyModel = RESTless.Model.extend({ _onPropertyChange: Ember.K }); -/* - * RecordArray - * Base class extention for arrays of Models - */ +/** + RecordArray is an Array of Model objects. + + @class RecordArray + @namespace RESTless + @extends Ember.ArrayProxy + @uses RESTless.State +*/ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { - /* - * adapter: hook for overriding the record array adapter + /** + The default adapter for the RecordArray. Providing a hook for overriding. + @property adapter */ adapter: Ember.computed(function() { return get(RESTless, 'client.adapter'); }).property('RESTless.client.adapter'), - /* - * deserializeMany: use the current Serializer turn the data into a record array + /** + Use the current Serializer to turn the data into a record array. + @method deserializeMany + @param {Object} type The type of model class + @param {Object} data The data to deserialize + @returns RESTless.RecordArray */ deserializeMany: function(type, data) { return get(this, 'adapter.serializer').deserializeMany(this, type, data); }, - /* - * serializeMany: use the current Serializer turn the array into data representation + + /** + Use the current Serializer to turn the array into its data representation. + @method serializeMany + @param {Object} type The type of model class + @returns RESTless.RecordArray */ serializeMany: function(type) { return get(this, 'adapter.serializer').serializeMany(this, type); }, - /* - * replaceContent: Changes array contents. Overriden to mark RecordArray as - * dirty if loaded. + /** + Overrides super replaceContent method to add isDirty functionality + @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. */ replaceContent: function(idx, amt, objects) { get(this, 'content').replace(idx, amt, objects); @@ -1225,20 +1705,20 @@ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { } }, - /* - * _onItemDirtyChange: (private) observes when items become dirty + /** + Observes when items become dirty and sets itself to dirty. + @private */ _onItemDirtyChange: Ember.observer(function() { - var clean = this.get('content').everyProperty('isDirty', false); + var clean = this.get('content').everyBy('isDirty', false); if(this.get('isLoaded') && !clean) { this.set('isDirty', true); } - return this; }, '@each.isDirty'), - /* - * _onLoadedChange: (private) observes when the array's isLoaded state changes - * and triggers each items onLoaded + /** + Observes when the array's isLoaded state changes and triggers each item's onLoaded. + @private */ _onLoadedChange: Ember.observer(function() { if(this.get('isLoaded')) { @@ -1251,19 +1731,23 @@ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { }, 'isLoaded') }); -/* - * RecordArray (static) - */ + RESTless.RecordArray.reopenClass({ - /* - * create: override state property defaults not implemented or applicable to arrays + /** + Creates a RecordArray + @method create + @returns RESTless.RecordArray */ create: function() { var arr = this._super.apply(this, arguments); - return arr.setProperties({ _isReady: true, isNew: false }); + // override State defaults not implemented or applicable to arrays + arr.setProperties({ _isReady: true, isNew: false }); + return arr; }, - /* - * createWithContent: helper to create a RecordArray with the content property initialized + /** + Helper to create a RecordArray with it's content property initialized to an Array + @method createWithContent + @returns RESTless.RecordArray */ createWithContent: function() { var arr = this.create.apply(this, arguments); @@ -1272,18 +1756,16 @@ RESTless.RecordArray.reopenClass({ } }); -/** - * Date.parse with progressive enhancement for ISO 8601 - * © 2011 Colin Snover - * Released under MIT license. - * - * From ember-data: - * https://github.com/emberjs/data/blob/master/packages/ember-data/lib/ext/date.js - */ - +/* + Date.parse with progressive enhancement for ISO 8601 + © 2011 Colin Snover + Released under MIT license. + Copied from: +*/ Ember.Date = Ember.Date || {}; var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; + Ember.Date.parse = function (date) { var timestamp, struct, minutesOffset = 0; @@ -1426,21 +1908,24 @@ RESTless.JSONTransforms = { }; /** - The FixtureAdapter is used for working with predefined - javascript data stored in memory. + The FixtureAdapter is used for working with predefined + javascript data stored in memory. - @class FixtureAdapter - @namespace RESTless - @extends RESTless.Adapter + @class FixtureAdapter + @beta + @namespace RESTless + @extends RESTless.Adapter */ RESTless.FixtureAdapter = RESTless.Adapter.extend({ serializer: RESTless.JSONSerializer.create(), /** + Saves a record. Pushes a new record to fixtures, or updates an existing record. @method saveRecord - @param record - */ + @param {RESTless.Model} record record to be saved + @return {Ember.RSVP.Promise} + */ saveRecord: function(record) { var isNew = record.get('isNew'), fixtures = record.constructor.FIXTURES, @@ -1490,9 +1975,11 @@ RESTless.FixtureAdapter = RESTless.Adapter.extend({ }, /** + Deletes a record from fixtures array @method deleteRecord - @param record - */ + @param {RESTless.Model} record record to be deleted + @return {Ember.RSVP.Promise} + */ deleteRecord: function(record) { var adapter = this; return new Ember.RSVP.Promise(function(resolve, reject){ @@ -1513,18 +2000,22 @@ RESTless.FixtureAdapter = RESTless.Adapter.extend({ }, /** + Finds all records of specified class in fixtures array @method findAll - @param klass - */ + @param {RESTless.Model} klass model type to find + @return {RESTless.RecordArray} + */ findAll: function(klass) { return this.findQuery(klass); }, /** + Finds records with specified query params in fixtures array @method findQuery - @param klass - @param queryParams - */ + @param {RESTless.Model} klass model type to find + @param {Object} queryParams hash of query params + @return {RESTless.RecordArray} + */ findQuery: function(klass, queryParams) { var fixtures = klass.FIXTURES, result = null, fixturesA; @@ -1552,10 +2043,12 @@ RESTless.FixtureAdapter = RESTless.Adapter.extend({ }, /** + Finds record with specified primary key in fixtures @method findByKey - @param klass - @param key - */ + @param {RESTless.Model} klass model type to find + @param {Number|String} key primary key value + @return {RESTless.Model} + */ findByKey: function(klass, key) { var fixtures = klass.FIXTURES, primaryKey = get(klass, 'primaryKey'), @@ -1578,8 +2071,10 @@ RESTless.FixtureAdapter = RESTless.Adapter.extend({ }, /** + Generates a uuid for a new record. @method generateIdForRecord - @param record + @param {Object} record + @return {String} uuid */ generateIdForRecord: function(record) { return parseInt(Ember.guidFor(record).match(/(\d+)$/)[0], 10); @@ -1588,7 +2083,6 @@ RESTless.FixtureAdapter = RESTless.Adapter.extend({ /** @method _findFixtureRecordIndex @private - @param record */ _findFixtureRecordIndex: function(record) { var klass = record.constructor, @@ -1643,8 +2137,11 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ dataStore = this._itemInsert(record); } else { dataStore[record.get(primaryKey)] = record.__data; - modelMeta.keys.push(record.get(primaryKey)); - this._updateModelMeta(modelMeta, dataStoreName); + // If key is not already stored, save it + if(modelMeta.keys.indexOf(record.get(primaryKey)) === -1) { + modelMeta.keys.push(record.get(primaryKey)); + this._updateModelMeta(modelMeta, dataStoreName); + } } try{ @@ -1671,14 +2168,16 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ modelMeta = this._getModelMeta(record); if(dataStore[key]) { - modelMeta.splice(modelMeta.indexOf(key), 1); + if(modelMeta && modelMeta.keys) { + modelMeta.keys.splice(modelMeta.keys.indexOf(key), 1); + } delete(dataStore[key]); try{ // Put the array back in LS localStorage.setItem(dataStoreName, JSON.stringify(dataStore)); this._updateModelMeta(modelMeta, dataStoreName); record.onDeleted(); - deferred.resolve(); + deferred.resolve(record); } catch (err) { record.onError(err); deferred.reject(err); @@ -1729,7 +2228,7 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ items = [], itemsA; for(var key in dataStore) { - if (dataStore[key]) { + if(dataStore[key]) { items.push(dataStore[key]); } } @@ -1745,7 +2244,7 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ return true; }); } - result.deserializeMany(model.toString(), items); + result.deserializeMany(model.toString(), itemsA); result.onLoaded(); return result; }, @@ -1775,8 +2274,12 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ deleteAll: function(model) { var deferred = Ember.RSVP.defer(), resourceName = get(model, 'resourceName'), - dataStore = localStorage.getItem(resourceName); + dataStore = localStorage.getItem(resourceName), + record = model.create({isNew: true}), + modelMeta = this._getModelMeta(record); + modelMeta.keys = []; + this._updateModelMeta(modelMeta, resourceName); if(dataStore) { try{ delete(localStorage[resourceName]); @@ -1802,6 +2305,35 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ } }, + /* + * Modifies circularLimit value. If we have more items, this will truncate + * the dataStore. -1 is for unlimited storage + */ + setCircularLimit: function(model, climit) { + var record = model.create({isNew: true}), + dataStoreName = this._getDSName(record), + dataStore = this._getDataStore(record), + modelMeta = this._getModelMeta(record), + keys = modelMeta.keys, + circularLimit = modelMeta.circularLimit; + + if(climit <= 0) { + modelMeta.circularLimit = -1; + } else { + // If we have more data than new limit, delete overflowing data + if(keys.length > climit) { + for(var i = 0; i < keys.length - climit; i++) { + delete(dataStore[keys[i]]); + } + localStorage.setItem(dataStoreName, JSON.stringify(dataStore)); + keys.splice(0, keys.length - climit); + modelMeta.keys = keys; + modelMeta.circularLimit = climit; + } + } + this._updateModelMeta(modelMeta, dataStoreName); + }, + /* * getDSName: Returns dataStore name for this resource in localStorage */ @@ -1913,7 +2445,7 @@ RESTless.LSAdapter = RESTless.Adapter.extend({ }); /* - * reopenClass to add deleteAll and updateCircularLimit as properties + * reopenClass to add deleteAll and setCircularLimit as properties */ RESTless.Model.reopenClass({ /* @@ -1921,8 +2453,15 @@ RESTless.Model.reopenClass({ */ deleteAll: function(params) { return get(this, 'adapter').deleteAll(this, params); + }, + + /* + * setCircularLimit: Updates circular limit value + */ + setCircularLimit: function(climit) { + return get(this, 'adapter').setCircularLimit(this, climit); } }); -})(this, jQuery, Ember); +})(this, jQuery, Ember); \ No newline at end of file diff --git a/web/external/ember-restless/ember-restless.js b/web/external/ember-restless/ember-restless.js index 3a05994..84d56cf 100644 --- a/web/external/ember-restless/ember-restless.js +++ b/web/external/ember-restless/ember-restless.js @@ -2,8 +2,8 @@ * ember-restless * A lightweight data persistence library for Ember.js * - * version: 0.4.0 - * last modifed: 2013-09-16 + * version: 0.5.0 + * last modifed: 2014-02-19 * * Garth Poitras * Copyright (c) 2013 Endless, Inc. @@ -13,46 +13,74 @@ "use strict"; +/** + @module ember-restless + */ var get = Ember.get, set = Ember.set, none = Ember.isNone, empty = Ember.isEmpty, + exports = Ember.exports || this, RESTless; -if (RESTless === undefined) { +if ('undefined' === typeof RESTless) { /** - * Create RESTless as an Ember Namespace. - * - * @class RESTless - * @static + All Ember RESTless functionality is defined inside of this namespace. + @class RESTless + @static */ - RESTless = Ember.Namespace.create(); + RESTless = Ember.Namespace.create({ + VERSION: '0.5.0' + }); - /** - * Expose RESTless to the global window namespace. - * Create shortcut alias 'RL'. + /* + A shortcut alias to the RESTless namespace. + Similar to `Ember` and `Em`. + Expose to global namespace. */ - if (window !== undefined) { - window.RL = window.RESTless = RESTless; + exports.RL = exports.RESTless = RESTless; + + if (Ember.libraries) { + Ember.libraries.register('Ember RESTless', RESTless.VERSION); } } /** - * Attributes - * Model property definitions - */ - -// Standard attribute + Defines an attribute on a Model. + Supports types: 'string', 'number', 'boolean', 'date'. + + @method attr + @for RESTless + @param {String} type the attribute type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.attr = function(type, opts) { var meta = Ember.merge({ type: type, isAttribute: true }, opts); return makeComputedAttribute(meta); }; -// belongsTo: One-to-one relationships +/** + Defines a one-to-one relationship attribute on a Model. + + @method belongsTo + @for RESTless + @param {String} type the belongsTo Class type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.belongsTo = function(type, opts) { var meta = Ember.merge({ type: type, isRelationship: true, belongsTo: true }, opts); return makeComputedAttribute(meta); }; -// hasMany: One-to-many & many-to-many relationships +/** + Defines a one-to-many relationship attribute on a Model. + + @method hasMany + @for RESTless + @param {String} type the hasMany Class type + @param {Object} [opts] a hash of options + @return {Ember.computed} attribute +*/ RESTless.hasMany = function(type, opts) { var defaultArray = function() { return RESTless.RecordArray.createWithContent(); @@ -89,58 +117,118 @@ function makeComputedAttribute(meta) { }).property('_data').meta(meta); } -/* - * Serializer - * Base serializer to be subclassed. - * Handles transforming data before saving to persistence layer - * and transforming data into Models when retrieving - */ +/** + Serializers handle transforming data to and from raw data and Models. + This is a base class to be subclassed. + + @class Serializer + @namespace RESTless + @extends Ember.Object +*/ RESTless.Serializer = Ember.Object.extend({ - /* - * dataType: i.e. json, jsonp, xml, html - */ + /** + Type of data to serialize. + @property dataType + @type String + @example json, jsonp, xml, html + */ dataType: null, - /* - * contentType: additional content type headers - */ + /** + Additional content type headers when transmitting data. + @property dataType + @type String + @optional + */ contentType: null, - /* - * Common serializer methods to be implemented in a subclass - */ - deserialize: Ember.K, + /** + Transforms raw data into model. Abstract - implement in subclass. + @method deserialize + */ + deserialize: Ember.K, + /** + Transforms raw data property into model property. Abstract - implement in subclass. + @method deserializeProperty + */ deserializeProperty: Ember.K, - deserializeMany: Ember.K, - serialize: Ember.K, - serializeProperty: Ember.K, - serializeMany: Ember.K, + /** + Transforms array of raw data into record array. Abstract - implement in subclass. + @method deserializeMany + */ + deserializeMany: Ember.K, + /** + Transforms model into raw data. Abstract - implement in subclass. + @method serialize + */ + serialize: Ember.K, + /** + Transforms model property into raw data property. Abstract - implement in subclass. + @method serializeProperty + */ + serializeProperty: Ember.K, + /** + Transforms a record array into raw data array. Abstract - implement in subclass. + @method serializeMany + */ + serializeMany: Ember.K, + /** + To register a custom attribute transform. Abstract - implement in subclass. + @method registerTransform + @optional + */ + registerTransform: Ember.K, - /* - * prepareData: (optional override) preps data before persisting - */ + /** + Optional override to prep data before persisting. + @method prepareData + @return Object + @optional + */ prepareData: function(data) { return data; }, - /* - * parseError: (optional override) deserialize error messages - */ + /** + Optional override to deserialize error messages. + @method parseError + @return Object + @optional + */ parseError: function(error) { return error; } }); -/* - * JSONSerializer - * Serializes and deserializes data to and from json - */ +/** + Handles transforming json data to Models and Models to json data. + + @class JSONSerializer + @namespace RESTless + @extends RESTless.Serializer +*/ RESTless.JSONSerializer = RESTless.Serializer.extend({ + /** + Type of data to serialize. + @property dataType + @type String + @default 'json' + */ dataType: 'json', + /** + Additional content type headers when transmitting data. + @property contentType + @type String + @default 'application/json; charset=utf-8' + */ contentType: 'application/json; charset=utf-8', - /* - * deserialize: translates json object into a model object - */ + /** + Transforms json object into model + @method deserialize + @param {RESTless.Model} resource model resource + @param {Object} data json data + @return {RESTless.Model} + */ deserialize: function(resource, data) { if(!data) { return resource; } @@ -166,15 +254,20 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ this.deserializeProperty(resource, prop, data[prop]); } } + resource.setProperties({ isLoaded: true, isDirty: false }); Ember.endPropertyChanges(resource); return resource; }, - /* - * deserializeProperty: sets model object properties from json - */ + /** + Transforms json key/value into model property + @method deserializeProperty + @param {RESTless.Model} resource model resource + @param {Object} prop json data key + @param {Object} value json data value + */ deserializeProperty: function(resource, prop, value) { - var attrName = this.attributeNameForKey(resource, prop), + var attrName = this.attributeNameForKey(resource.constructor, prop), fields = get(resource.constructor, 'fields'), field = fields.get(attrName), type, klass; @@ -191,8 +284,7 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ } // If property is a belongsTo relationship, deserialze that model else if (field.belongsTo && klass && value) { - var belongsToModel = klass.create({ isNew: false }).deserialize(value); - belongsToModel.onLoaded(); + var belongsToModel = klass.create({ isNew: false, isLoaded: true }).deserialize(value); resource.set(attrName, belongsToModel); } else { @@ -204,55 +296,62 @@ RESTless.JSONSerializer = RESTless.Serializer.extend({ } }, - /* - * deserializeMany: deserializes an array of json objects - */ + /** + Transforms json array into a record array + @method deserializeMany + @param {RESTless.RecordArray} recordArray RecordArray + @param {String} type records class name + @param {Object} data json data + @return {RESTless.RecordArray} + */ deserializeMany: function(recordArray, type, data) { if(!data) { return recordArray; } - var klass = get(Ember.lookup, type), meta, keyPlural, len, i, item; + var klass = get(Ember.lookup, type), + arrayData = this._arrayDataForType(type, data), + meta, i, len, item, content; - // extract any meta info - meta = this.extractMeta(data); - if(meta) { recordArray.set('meta', meta); } - - // Check for wrapped array by resource name: { posts: [...] } - // This is the default from ActiveRecord on direct finds - if(!Ember.isArray(data)) { - keyPlural = this._keyPluralForResourceType(type); - if(data[keyPlural]) { - data = data[keyPlural]; - } else { - return recordArray; - } - } + if(!arrayData) { return recordArray; } if(recordArray) { + recordArray.set('isLoaded', false); recordArray.clear(); } else { recordArray = RESTless.RecordArray.createWithContent(); } - len = data.length; + len = arrayData.length; if(len) { - Ember.beginPropertyChanges(recordArray); + content = []; for(i=0; i + App.Adapter.map('App.Post', { primaryKey: 'slug' }); + App.Adapter.map('App.Person', { lastName: { key: 'lastNameOfPerson' } }); + */ map: function(modelKey, config) { var modelMap = this.get('configurations.models'), modelConfig = modelMap.get(modelKey), @@ -526,124 +731,205 @@ RESTless.Adapter = Ember.Object.extend({ return this; }, - /* - * pluralize: helper to pluralize model resource names. - * Checks custom configs or simply appends a 's' - */ + /** + Helper to pluralize a model resource names. + Checks custom configs or simply appends a 's'. + @method pluralize + @param {String} resourceName Name of model class + @return {String} plural name + */ pluralize: function(resourceName) { var plurals = this.get('configurations.plurals'); return (plurals && plurals[resourceName]) || resourceName + 's'; } }); -/* - * RESTAdapter - * Builds REST urls to resources - * Builds and handles remote ajax requests - */ +/** + The REST Adapter handles sending and fetching data to and from a REST API. + + @class RESTAdapter + @namespace RESTless + @extends RESTless.Adapter +*/ RESTless.RESTAdapter = RESTless.Adapter.extend({ - /* - * serializer: default to a JSON serializer + /** + Serializer used to transform data. + @property serializer + @type RESTless.Serializer + @default RESTless.JSONSerializer */ serializer: RESTless.JSONSerializer.create(), - /* - * url: base url of backend REST service - * example: 'https://api.example.com' + /** + Host url of the REST API if on a different domain than the app. + @property host + @type String + @optional + @example 'http://api.example.com' + */ + host: Ember.computed.oneWay('url'), + /** + Deprecated. + @property url + @type String + @deprecated Use: `host` */ url: null, - /* - * namespace: endpoint path - * example: 'api/v1' + + /** + API namespace endpoint path + @property namespace + @type String + @optional + @example 'api/v1' */ namespace: null, - /* - * useContentTypeExtension: forces content type extensions on resource requests - * i.e. /posts.json vs /posts | /posts/115.json vs /posts/115 - * Useful for conforming to 3rd party apis - * or returning correct content-type headers with Rails caching + + /** + If an API requires certain headers to be transmitted, e.g. an api key, + you can add a hash of headers to be sent on each request. + @property headers + @type Object + @optional + @example '{ "X-API-KEY" : "abc1234" }' + */ + headers: null, + /** + If an API requires paramters to be set on every request, + e.g. an api key, you can add a hash of defaults. + @property defaultData + @type Object + @optional + @example '{ api_key: "abc1234" }' + */ + defaultData: null, + + /** + Adds content type extensions to requests. + @property useContentTypeExtension + @type Boolean + @default false + @example + When `true` will make requests `/posts.json` instead of `/posts` or `/posts/115.json` instead of `/posts/115` */ useContentTypeExtension: false, - /* - * rootPath: computed path based on url and namespace + /** + Computed path based on host and namespace. + @property rootPath + @type String + @final */ rootPath: Ember.computed(function() { var a = document.createElement('a'), - url = this.get('url'), + host = this.get('host'), ns = this.get('namespace'), rootReset = ns && ns.charAt(0) === '/'; - a.href = url ? url : ''; + a.href = host ? host : '/'; if(ns) { a.pathname = rootReset ? ns : (a.pathname + ns); } return a.href.replace(/\/+$/, ''); - }).property('url', 'namespace'), + }).property('host', 'namespace'), - /* - * resourcePath: helper method creates a valid REST path to a resource - * App.Post => 'posts', App.PostGroup => 'post_groups' + /** + Helper method creates a valid REST path to a resource + @method resourcePath + @param {String} resourceName Type of Model + @return {String} the resource path + @example App.Post => 'posts', App.PostGroup => 'post_groups' */ resourcePath: function(resourceName) { return this.pluralize(Ember.String.decamelize(resourceName)); }, - /* - * request: creates and executes an ajax request wrapped in a promise + /** + Creates and executes an ajax request wrapped in a promise. + @method request + @param {RESTless.Model} model model to use to build the request + @param {Object} [params] Additional ajax params + @param {Object} [key] optional resource primary key value + @return {Ember.RSVP.Promise} */ request: function(model, params, key) { - var adapter = this, serializer = this.serializer; + var adapter = this, + ajaxParams = this.prepareParams(params); + ajaxParams.url = this.buildUrl(model, key); return new Ember.RSVP.Promise(function(resolve, reject) { - params = params || {}; - params.url = adapter.buildUrl(model, key); - params.dataType = serializer.dataType; - params.contentType = serializer.contentType; - - if(params.data && params.type !== 'GET') { - params.data = serializer.prepareData(params.data); - } - - params.success = function(data, textStatus, jqXHR) { + ajaxParams.success = function(data) { Ember.run(null, resolve, data); }; - params.error = function(jqXHR, textStatus, errorThrown) { + ajaxParams.error = function(jqXHR, textStatus, errorThrown) { var errors = adapter.parseAjaxErrors(jqXHR, textStatus, errorThrown); Ember.run(null, reject, errors); }; - var ajax = Ember.$.ajax(params); - + var ajax = Ember.$.ajax(ajaxParams); // (private) store current ajax request on the model. - model.set('currentRequest', ajax); + model.currentRequest = ajax; }); }, - /* - * buildUrl (private): constructs request url and dynamically adds the a resource key if specified + /** + Builds ajax request parameters + @method prepareParams + @param {Object} [params] base ajax params + @return {Object} + @private + */ + prepareParams: function(params) { + var serializer = this.serializer, + headers = this.get('headers'), + defaultData = this.get('defaultData'); + params = params || {}; + params.dataType = serializer.dataType; + params.contentType = serializer.contentType; + if(headers) { + params.headers = headers; + } + if(defaultData) { + params.data = $.extend({}, defaultData, params.data); + } + if(params.data && params.type !== 'GET') { + params.data = serializer.prepareData(params.data); + } + return params; + }, + + /** + Constructs request url and dynamically adds the resource key if specified + @method buildUrl + @private */ buildUrl: function(model, key) { var resourcePath = this.resourcePath(get(model.constructor, 'resourceName')), primaryKey = get(model.constructor, 'primaryKey'), urlParts = [this.get('rootPath'), resourcePath], - dataType = this.get('serializer.dataType'), url; + dataType, url; if(key) { urlParts.push(key); } else if(model.get(primaryKey)) { urlParts.push(model.get(primaryKey)); } - url = urlParts.join('/'); - if(this.get('useContentTypeExtension') && dataType) { - url += '.' + dataType; + + if(this.useContentTypeExtension) { + dataType = this.serializer.dataType; + if(dataType) { + url += '.' + dataType; + } } return url; }, - /* - * saveRecord: POSTs a new record, or PUTs an updated record to REST service + /** + Saves a record. POSTs a new record, or PUTs an updated record to REST API + @method saveRecord + @param {RESTless.Model} record record to be saved + @return {Ember.RSVP.Promise} */ saveRecord: function(record) { var isNew = record.get('isNew'), method, ajaxPromise; @@ -672,6 +958,12 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Deletes a record from REST API using DELETE + @method deleteRecord + @param {RESTless.Model} record record to be deleted + @return {Ember.RSVP.Promise} + */ deleteRecord: function(record) { var ajaxPromise = this.request(record, { type: 'DELETE', data: record.serialize() }); @@ -686,6 +978,12 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Reloads a record from REST API + @method reloadRecord + @param {RESTless.Model} record record to be reloaded + @return {Ember.RSVP.Promise} + */ reloadRecord: function(record) { var klass = record.constructor, primaryKey = get(klass, 'primaryKey'), @@ -710,10 +1008,23 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return ajaxPromise; }, + /** + Finds all records of specified class using GET + @method findAll + @param {RESTless.Model} klass model type to find + @return {RESTless.RecordArray} + */ findAll: function(klass) { return this.findQuery(klass); }, + /** + Finds records with specified query params using GET + @method findQuery + @param {RESTless.Model} klass model type to find + @param {Object} queryParams hash of query params + @return {RESTless.RecordArray} + */ findQuery: function(klass, queryParams) { var type = klass.toString(), resourceInstance = klass.create({ isNew: false }), @@ -730,6 +1041,14 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return result; }, + /** + Finds record with specified primary key using GET + @method findByKey + @param {RESTless.Model} klass model type to find + @param {Number|String} key primary key value + @param {Object} [queryParams] hash of additional query params + @return {RESTless.Model} + */ findByKey: function(klass, key, queryParams) { var result = klass.create({ isNew: false }), ajaxPromise = this.request(result, { type: 'GET', data: queryParams }, key); @@ -744,29 +1063,20 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ return result; }, - /* - * fetch: wraps find method in a promise for async find support - * Overridden to add currentRequest - */ - fetch: function(klass, params) { - var adapter = this, find, promise; - promise = new Ember.RSVP.Promise(function(resolve, reject) { - find = adapter.find(klass, params); - find.one('didLoad', function(model) { - resolve(model); - }); - find.one('becameError', function(error) { - reject(error); - }); - }); - // private: store the ajax request for aborting, etc. - promise._currentRequest = find.get('currentRequest'); - return promise; + /** + Registers custom attribute transforms. + Fowards creation to serializer. + @method registerTransform + */ + registerTransform: function(type, transform) { + this.get('serializer').registerTransform(type, transform); }, - /* - * parseAjaxErrors: builds a robust error object using the serializer and xhr data - */ + /** + Builds a robust error object using the serializer and xhr data + @method parseAjaxErrors + @private + */ parseAjaxErrors: function(jqXHR, textStatus, errorThrown) { // use serializer to parse error messages from server var errors = this.get('serializer').parseError(jqXHR.responseText) || {}; @@ -776,29 +1086,29 @@ RESTless.RESTAdapter = RESTless.Adapter.extend({ errors.textStatus = textStatus; errors.errorThrown = errorThrown; return errors; - }, - - /* - * registerTransform: fowards custom tranform creation to serializer - */ - registerTransform: function(type, transform) { - this.get('serializer').registerTransform(type, transform); } }); /** - * Client - * You can define a custom Client on your app like you would DS.Store in ember-data. - * The client will be automatically detected and set from your App namespace. - * Setting a Client is optional and will automatically use a base Client. - * - * @class Client - * @namespace RESTless - * @extends Ember.Object - */ + The Client is the top level store, housing the default adapter and configurations. + The client will be automatically detected and set from your App namespace. + Setting a client is optional and will automatically use a base client. + + @class Client + @namespace RESTless + @extends Ember.Object +*/ RESTless.Client = Ember.Object.extend({ + /** + The default adapter for all models. + @property adapter + @type RESTless.Adapter + */ adapter: RESTless.RESTAdapter.create(), - // Private shortcut aliases: + /** + Shortcut alias to model configurations + @private + */ _modelConfigs: Ember.computed.alias('adapter.configurations.models') }); @@ -806,15 +1116,8 @@ Ember.onLoad('Ember.Application', function(Application) { Application.initializer({ name: 'Client', initialize: function(container, application) { - // On app initialize, if custom Client is present, - // set that as the default client - if(application.Client) { - RESTless.set('client', application.Client); - } else { - // Set a default client - RESTless.set('client', RESTless.Client.create()); - } - // Add an observer so you can set a client at a later date + var client = application.Client ? application.Client : RESTless.Client.create(); + RESTless.set('client', client); application.addObserver('Client', this, function() { RESTless.set('client', this.Client); }); @@ -822,52 +1125,83 @@ Ember.onLoad('Ember.Application', function(Application) { }); }); -/* - * State - * Mixin for managing model lifecycle state - */ +/** + The State Mixin is responsible for keeping state and + handling state events for Models. + + @class State + @namespace RESTless + @uses Ember.Evented +*/ RESTless.State = Ember.Mixin.create( Ember.Evented, { - /* - * isNew: model has not yet been saved. - */ + /** + Model has not yet been saved; not yet assigned a primary key. + @property isNew + @type {Boolean} + */ isNew: true, - /* - * isLoaded: model has been retrieved - */ + /** + Model has been retrieved and properties set. + @property isLoaded + @type {Boolean} + */ isLoaded: false, - /* - * isDirty: model has changes that have not yet been saved - */ + /** + Model has changes that have not yet been saved. + @property isDirty + @type {Boolean} + */ isDirty: false, - /* - * isSaving: model is in the process of saving - */ + /** + Model model is in the process of saving. + @property isSaving + @type {Boolean} + */ isSaving: false, - /* - * isError: model has been marked as invalid after response from adapter - */ + /** + Model model is in error state. + @property isError + @type {Boolean} + */ isError: false, - /* - * _isReady (private) - * Flag for deferring dirty state when setting initial values on create() or load() - */ - _isReady: false, - /* - * errors: error data returned from adapter - */ + + /** + Hash of current errors on model. + @property errors + @type Object + */ errors: null, - /* - * State event hooks - */ - didCreate: Ember.K, - didUpdate: Ember.K, - didLoad: Ember.K, - didDelete: Ember.K, - becameError: Ember.K, + /** + Fired when the record is created. + @event didCreate + */ + didCreate: Ember.K, + /** + Fired when the record is updated. + @event didUpdate + */ + didUpdate: Ember.K, + /** + Fired when the record is enters the loaded state. + @event didLoad + */ + didLoad: Ember.K, + /** + Fired when the record is deleted. + @event didDelete + */ + didDelete: Ember.K, + /** + Fired when the record enters the error state. + @event becameError + */ + becameError: Ember.K, - /* - * Internal state change handlers, called by adapter + /** + Updates state and triggers events upon saving. + @method onSaved + @param {Boolean} wasNew was a new model prior to saving. */ onSaved: function(wasNew) { this.setProperties({ @@ -881,6 +1215,10 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('didLoad', this); }, + /** + Updates state and triggers events upon deletion. + @method onDeleted + */ onDeleted: function() { this._triggerEvent('didDelete', this); Ember.run.next(this, function() { @@ -888,8 +1226,14 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { }); }, + /** + Updates state and triggers events upon loading. + @method onLoaded + */ onLoaded: function() { this.setProperties({ + isDirty: false, + isSaving: false, isLoaded: true, isError: false, errors: null @@ -897,6 +1241,10 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('didLoad', this); }, + /** + Updates state and triggers events upon an error. + @method onError + */ onError: function(errors) { this.setProperties({ isSaving: false, @@ -906,16 +1254,21 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { this._triggerEvent('becameError', errors); }, - /* - * clearErrors: (helper) reset isError flag, clear error messages + /** + Clears errors and resets error state + @method clearErrors + @returns {Object} */ clearErrors: function() { this.setProperties({ isError: false, errors: null }); return this; }, - /* - * copyState: copies the current state to a cloned object + /** + Copies the current state to a cloned object + @method copyState + @param {Object} clone the cloned object + @returns {Object} the cloned object with copied state */ copyState: function(clone) { var mi = RESTless.State.mixins, @@ -930,6 +1283,19 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { return clone; }, + /** + Flag for deferring dirty state when setting initial values on create() or load() + @property _isReady + @type {Boolean} + @private + */ + _isReady: false, + + /** + Helper function to trigger events on models and to any listeners. + @method _triggerEvent + @private + */ _triggerEvent: function(event, data) { Ember.run(this, function() { Ember.tryInvoke(this, event, [data]); @@ -939,27 +1305,24 @@ RESTless.State = Ember.Mixin.create( Ember.Evented, { }); /** - * Model - * Base model class for all records - * - * @class Model - * @namespace RESTless - * @extends Ember.Object - * @constructor - */ + The base model class for all RESTless objects. + + @class Model + @namespace RESTless + @extends Ember.Object + @uses RESTless.State + @uses Ember.Copyable +*/ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { /** - * id: unique id number, default primary id - * - * @property {RESTless.attr} + A unique id number for the record. `id` is the default primary key. + @property id */ id: RESTless.attr('number'), /** - * _data: Stores raw model data. Don't use directly; use declared model attributes. - * - * @private - * @property {Object} + Stores raw model data. Don't use directly; use declared model attributes. + @private */ __data: null, _data: Ember.computed(function() { @@ -968,7 +1331,8 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }), /** - * didDefineProperty: Hook to add observers for each attribute/relationship for 'isDirty' functionality + Hook to add observers for each attribute/relationship for 'isDirty' functionality + @protected */ didDefineProperty: function(proto, key, value) { if (value instanceof Ember.Descriptor) { @@ -982,9 +1346,9 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }, /** - * _onPropertyChange: called when any property of the model changes - * If the model has been loaded, or is new, isDirty flag is set to true. - * @private + _onPropertyChange: called when any property of the model changes + If the model has been loaded, or is new, isDirty flag is set to true. + @private */ _onPropertyChange: function(key) { var isNew = this.get('isNew'); @@ -1000,9 +1364,9 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { } }, /** - * _onRelationshipChange: called when a relationship property's isDirty state changes - * Forwards a _onPropertyChange event for the parent object - * @private + Called when a relationship property's isDirty state changes. + Forwards a _onPropertyChange event for the parent object. + @private */ _onRelationshipChange: function(sender, key) { if(sender.get(key)) { // if isDirty @@ -1011,8 +1375,11 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { }, /** - * copy: creates a copy of the object. Implements Ember.Copyable protocol - * http://emberjs.com/api/classes/Ember.Copyable.html#method_copy + Creates a clone of the model. Implements Ember.Copyable protocol + + @method copy + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver */ copy: function(deep) { var clone = this.constructor.create(), @@ -1029,73 +1396,120 @@ RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { return clone; }, - /* - * copyWithState: creates a copy of the object along with the RESTless.State properties + + /** + Creates a clone copy of the model along with it's current State. + @method copyWithState + @param {Boolean} deep if `true`, a deep copy of the object should be made + @return {Object} copy of receiver */ copyWithState: function(deep) { return this.copyState(this.copy(deep)); }, - /* - * create/update/delete methods - * Forward to the current adapter to perform operations on persistance layer - */ + /** + Saves the record using the model's adapter. + @method saveRecord + @chainable + */ saveRecord: function() { return get(this.constructor, 'adapter').saveRecord(this); }, + /** + Deletes the record using the model's adapter. + @method deleteRecord + @chainable + */ deleteRecord: function() { return get(this.constructor, 'adapter').deleteRecord(this); }, + /** + Reloads the record using the model's adapter. + @method reloadRecord + @chainable + */ reloadRecord: function() { return get(this.constructor, 'adapter').reloadRecord(this); }, - /* - * serialization methods: Transforms model to and from its data representation. - * Forward to the current serializer to perform appropriate parsing - */ + /** + Serializes the record into its data representaion. + @method serialize + @param {Object} options hash of serialization options + @chainable + */ serialize: function(options) { return RESTless.get('client.adapter.serializer').serialize(this, options); }, + /** + Deserializes raw data into Model properties + @method deserialize + @param {Object} data raw data to deserialize + @chainable + */ deserialize: function(data) { return RESTless.get('client.adapter.serializer').deserialize(this, data); }, + /** + Serializes a Model property into its data representaion. + @method serializeProperty + @param {String} prop property key + @chainable + */ serializeProperty: function(prop) { return RESTless.get('client.adapter.serializer').serializeProperty(this, prop); }, + /** + Deserializes raw data property into Model property + @method deserializeProperty + @param {String} prop property key + @param value property value + @chainable + */ deserializeProperty: function(prop, value) { return RESTless.get('client.adapter.serializer').deserializeProperty(this, prop, value); } }); -/* - * RESTless.Model (static) - * Class level properties and methods - */ +/** + Static properties and methods for the Model Class. + + @class Model + @namespace RESTless +*/ RESTless.Model.reopenClass({ - /* - * create: standard super class create, then marks _isReady state flag + /** + Extends super class `create` and marks _isReady state. + @method create + @return RESTless.Model */ create: function() { var instance = this._super.apply(this, arguments); instance.set('_isReady', true); return instance; }, - /* - * createRecord: alias to create. Ease transition to/from ember-data + /** + Alias to `create`. Eases transition to/from ember-data + @deprecated Use `create` + @method createRecord + @return RESTless.Model */ createRecord: Ember.aliasMethod('create'), - /* - * adapter: hook to override which adapter instance to use per model + /** + The adapter for the Model. Provides a hook for overriding. + @property adapter + @type RESTless.Adapter */ adapter: Ember.computed(function() { return get(RESTless, 'client.adapter'); }).property('RESTless.client.adapter'), - /* - * primaryKey: property name for the primary key. - * Configurable. Defaults to 'id'. + /** + The property name for the primary key + @property primaryKey + @type String + @default 'id' */ primaryKey: Ember.computed(function() { var className = this.toString(), @@ -1106,17 +1520,34 @@ RESTless.Model.reopenClass({ return 'id'; }).property('RESTless.client._modelConfigs'), - /* - * resourceName: helper to extract name of model subclass - * App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' + /** + The name of the resource, derived from the class name. + App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' + + @property resourceName + @type String */ resourceName: Ember.computed(function() { var classNameParts = this.toString().split('.'); return classNameParts[classNameParts.length-1]; }), + /** + The plural name of the resource, derived from the class name. + App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' - /* - * fields: meta information for all attributes and relationships + @property resourceNamePlural + @type String + */ + resourceNamePlural: Ember.computed(function() { + var resourceName = get(this, 'resourceName'), + adapter = get(this, 'adapter'); + return adapter.pluralize(Ember.String.decamelize(resourceName)); + }), + + /** + Meta information for all attributes and relationships + @property fields + @type Ember.Map */ fields: Ember.computed(function() { var map = Ember.Map.create(); @@ -1128,42 +1559,76 @@ RESTless.Model.reopenClass({ return map; }), - /* - * find methods: retrieve model(s) with specified params - * Forwards to the current adapter to retrieve from the appropriate data layer + /** + Find resources using the adapter. + This method can handle all find types: `findAll`, `findQuery`, `findByKey` + @method find + @param {Object} params + @return Object */ find: function(params) { return get(this, 'adapter').find(this, params); }, + /** + Finds resources using the adapter, and returns a promise. + @method fetch + @param {Object} params hash of query params + @return Ember.RSVP.Promise + */ fetch: function(params) { return get(this, 'adapter').fetch(this, params); }, + /** + Finds all resources of this type using the adapter. + @method findAll + @return Object + */ findAll: function() { return get(this, 'adapter').findAll(this); }, + /** + Find resources with query using the adapter. + @method findQuery + @param {Object} params hash of query params + @return Object + */ findQuery: function(params) { return get(this, 'adapter').findQuery(this, params); }, + /** + Find resource with specified primary key using the adapter. + @method findByKey + @param {Number|String} key + @param {Object} params any additional params + @return Object + */ findByKey: function(key, params) { return get(this, 'adapter').findByKey(this, key, params); }, - /* - * findById: alias to findByKey method - * Keeps api inline with ember-data. - * A model's primary key can be customized so findById is not always semantically correct. + /** + Find resource with specified id using the adapter. + Keeps API similar to ember-data. + @method findById + @deprecated Use `findByKey` */ findById: Ember.aliasMethod('findByKey'), - /* - * load: Create model directly from data representation. + /** + Create model directly from data representation. + @method load + @param {Object} data raw data to load + @return RESTless.Model */ load: function(data) { var model = this.create().set('_isReady', false).deserialize(data).set('_isReady', true); model.onLoaded(); return model; }, - /* - * loadMany: Create collection of records directly from data representation. + /** + Create collection of records directly from data representation.. + @method loadMany + @param {Object} data raw data to load + @return RESTless.RecordArray */ loadMany: function(data) { var array = RESTless.RecordArray.createWithContent().deserializeMany(this.toString(), data); @@ -1172,16 +1637,15 @@ RESTless.Model.reopenClass({ } }); -/* - * ReadOnlyModel - * Subclass for models that are read-only. - * Removes property change observers and write methods. - * Helps improve performance when write functionality is not needed. - */ +/** + A read-only model. Removes property change observers and write methods. + Helps improve performance when write functionality is not needed. + + @class ReadOnlyModel + @namespace RESTless + @extends RESTless.Model +*/ RESTless.ReadOnlyModel = RESTless.Model.extend({ - /* - * Remove functionality associated with writing data and keeping state - */ serialize: null, saveRecord: null, deleteRecord: null, @@ -1189,34 +1653,50 @@ RESTless.ReadOnlyModel = RESTless.Model.extend({ _onPropertyChange: Ember.K }); -/* - * RecordArray - * Base class extention for arrays of Models - */ +/** + RecordArray is an Array of Model objects. + + @class RecordArray + @namespace RESTless + @extends Ember.ArrayProxy + @uses RESTless.State +*/ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { - /* - * adapter: hook for overriding the record array adapter + /** + The default adapter for the RecordArray. Providing a hook for overriding. + @property adapter */ adapter: Ember.computed(function() { return get(RESTless, 'client.adapter'); }).property('RESTless.client.adapter'), - /* - * deserializeMany: use the current Serializer turn the data into a record array + /** + Use the current Serializer to turn the data into a record array. + @method deserializeMany + @param {Object} type The type of model class + @param {Object} data The data to deserialize + @returns RESTless.RecordArray */ deserializeMany: function(type, data) { return get(this, 'adapter.serializer').deserializeMany(this, type, data); }, - /* - * serializeMany: use the current Serializer turn the array into data representation + + /** + Use the current Serializer to turn the array into its data representation. + @method serializeMany + @param {Object} type The type of model class + @returns RESTless.RecordArray */ serializeMany: function(type) { return get(this, 'adapter.serializer').serializeMany(this, type); }, - /* - * replaceContent: Changes array contents. Overriden to mark RecordArray as - * dirty if loaded. + /** + Overrides super replaceContent method to add isDirty functionality + @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. */ replaceContent: function(idx, amt, objects) { get(this, 'content').replace(idx, amt, objects); @@ -1225,20 +1705,20 @@ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { } }, - /* - * _onItemDirtyChange: (private) observes when items become dirty + /** + Observes when items become dirty and sets itself to dirty. + @private */ _onItemDirtyChange: Ember.observer(function() { - var clean = this.get('content').everyProperty('isDirty', false); + var clean = this.get('content').everyBy('isDirty', false); if(this.get('isLoaded') && !clean) { this.set('isDirty', true); } - return this; }, '@each.isDirty'), - /* - * _onLoadedChange: (private) observes when the array's isLoaded state changes - * and triggers each items onLoaded + /** + Observes when the array's isLoaded state changes and triggers each item's onLoaded. + @private */ _onLoadedChange: Ember.observer(function() { if(this.get('isLoaded')) { @@ -1251,19 +1731,23 @@ RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { }, 'isLoaded') }); -/* - * RecordArray (static) - */ + RESTless.RecordArray.reopenClass({ - /* - * create: override state property defaults not implemented or applicable to arrays + /** + Creates a RecordArray + @method create + @returns RESTless.RecordArray */ create: function() { var arr = this._super.apply(this, arguments); - return arr.setProperties({ _isReady: true, isNew: false }); + // override State defaults not implemented or applicable to arrays + arr.setProperties({ _isReady: true, isNew: false }); + return arr; }, - /* - * createWithContent: helper to create a RecordArray with the content property initialized + /** + Helper to create a RecordArray with it's content property initialized to an Array + @method createWithContent + @returns RESTless.RecordArray */ createWithContent: function() { var arr = this.create.apply(this, arguments); @@ -1272,18 +1756,16 @@ RESTless.RecordArray.reopenClass({ } }); -/** - * Date.parse with progressive enhancement for ISO 8601 - * © 2011 Colin Snover - * Released under MIT license. - * - * From ember-data: - * https://github.com/emberjs/data/blob/master/packages/ember-data/lib/ext/date.js - */ - +/* + Date.parse with progressive enhancement for ISO 8601 + © 2011 Colin Snover + Released under MIT license. + Copied from: +*/ Ember.Date = Ember.Date || {}; var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; + Ember.Date.parse = function (date) { var timestamp, struct, minutesOffset = 0; From 336d378dc281c8d58d9afff019c0288f18e6bd1c Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 26 Feb 2014 21:22:07 +0530 Subject: [PATCH 16/68] Add ember-restless to MANIFEST and import only ember-restless+extras.js --- web/external/MANIFEST | 7 + web/external/ember-restless/ember-restless.js | 1910 ----------------- 2 files changed, 7 insertions(+), 1910 deletions(-) delete mode 100644 web/external/ember-restless/ember-restless.js diff --git a/web/external/MANIFEST b/web/external/MANIFEST index a79f8ed..dba5fca 100644 --- a/web/external/MANIFEST +++ b/web/external/MANIFEST @@ -60,6 +60,13 @@ date: 2013-05-24 license: MIT url: https://raw.github.com/emberjs/ember.js/release-builds/ember-1.0.0-rc.3.js +name: ember-restless +site: http://endlessinc.github.io/ember-restless/ +version: 0.5.0 +date: 2014-02-26 +license: MIT +url: https://raw.github.com/endlessinc/ember-restless/v0.5.0/dist/ember-restless+extras.js + name: d3 site: https://github.com/mbostock/d3 version: v3 diff --git a/web/external/ember-restless/ember-restless.js b/web/external/ember-restless/ember-restless.js deleted file mode 100644 index 84d56cf..0000000 --- a/web/external/ember-restless/ember-restless.js +++ /dev/null @@ -1,1910 +0,0 @@ -/** - * ember-restless - * A lightweight data persistence library for Ember.js - * - * version: 0.5.0 - * last modifed: 2014-02-19 - * - * Garth Poitras - * Copyright (c) 2013 Endless, Inc. - */ - -(function(window, $, Ember, undefined){ - -"use strict"; - -/** - @module ember-restless - */ -var get = Ember.get, set = Ember.set, - none = Ember.isNone, empty = Ember.isEmpty, - exports = Ember.exports || this, - RESTless; - -if ('undefined' === typeof RESTless) { - /** - All Ember RESTless functionality is defined inside of this namespace. - @class RESTless - @static - */ - RESTless = Ember.Namespace.create({ - VERSION: '0.5.0' - }); - - /* - A shortcut alias to the RESTless namespace. - Similar to `Ember` and `Em`. - Expose to global namespace. - */ - exports.RL = exports.RESTless = RESTless; - - if (Ember.libraries) { - Ember.libraries.register('Ember RESTless', RESTless.VERSION); - } -} - -/** - Defines an attribute on a Model. - Supports types: 'string', 'number', 'boolean', 'date'. - - @method attr - @for RESTless - @param {String} type the attribute type - @param {Object} [opts] a hash of options - @return {Ember.computed} attribute -*/ -RESTless.attr = function(type, opts) { - var meta = Ember.merge({ type: type, isAttribute: true }, opts); - return makeComputedAttribute(meta); -}; - -/** - Defines a one-to-one relationship attribute on a Model. - - @method belongsTo - @for RESTless - @param {String} type the belongsTo Class type - @param {Object} [opts] a hash of options - @return {Ember.computed} attribute -*/ -RESTless.belongsTo = function(type, opts) { - var meta = Ember.merge({ type: type, isRelationship: true, belongsTo: true }, opts); - return makeComputedAttribute(meta); -}; - -/** - Defines a one-to-many relationship attribute on a Model. - - @method hasMany - @for RESTless - @param {String} type the hasMany Class type - @param {Object} [opts] a hash of options - @return {Ember.computed} attribute -*/ -RESTless.hasMany = function(type, opts) { - var defaultArray = function() { - return RESTless.RecordArray.createWithContent(); - }, - meta = Ember.merge({ type: type, isRelationship: true, hasMany: true, defaultValue: defaultArray }, opts); - return makeComputedAttribute(meta); -}; - -function makeComputedAttribute(meta) { - return Ember.computed(function(key, value) { - var data = this.get('_data'); - // Getter - if (arguments.length === 1) { - value = data[key]; - - if (value === undefined) { - // Default values - if (typeof meta.defaultValue === 'function') { - value = meta.defaultValue(); - } else { - value = meta.defaultValue; - } - data[key] = value; - } - } - // Setter - else if (value !== data[key]) { - data[key] = value; - if (!meta.readOnly && !RESTless.ReadOnlyModel.detectInstance(this)) { - this._onPropertyChange(key); - } - } - return value; - }).property('_data').meta(meta); -} - -/** - Serializers handle transforming data to and from raw data and Models. - This is a base class to be subclassed. - - @class Serializer - @namespace RESTless - @extends Ember.Object -*/ -RESTless.Serializer = Ember.Object.extend({ - /** - Type of data to serialize. - @property dataType - @type String - @example json, jsonp, xml, html - */ - dataType: null, - /** - Additional content type headers when transmitting data. - @property dataType - @type String - @optional - */ - contentType: null, - - /** - Transforms raw data into model. Abstract - implement in subclass. - @method deserialize - */ - deserialize: Ember.K, - /** - Transforms raw data property into model property. Abstract - implement in subclass. - @method deserializeProperty - */ - deserializeProperty: Ember.K, - /** - Transforms array of raw data into record array. Abstract - implement in subclass. - @method deserializeMany - */ - deserializeMany: Ember.K, - /** - Transforms model into raw data. Abstract - implement in subclass. - @method serialize - */ - serialize: Ember.K, - /** - Transforms model property into raw data property. Abstract - implement in subclass. - @method serializeProperty - */ - serializeProperty: Ember.K, - /** - Transforms a record array into raw data array. Abstract - implement in subclass. - @method serializeMany - */ - serializeMany: Ember.K, - /** - To register a custom attribute transform. Abstract - implement in subclass. - @method registerTransform - @optional - */ - registerTransform: Ember.K, - - /** - Optional override to prep data before persisting. - @method prepareData - @return Object - @optional - */ - prepareData: function(data) { - return data; - }, - /** - Optional override to deserialize error messages. - @method parseError - @return Object - @optional - */ - parseError: function(error) { - return error; - } -}); - -/** - Handles transforming json data to Models and Models to json data. - - @class JSONSerializer - @namespace RESTless - @extends RESTless.Serializer -*/ -RESTless.JSONSerializer = RESTless.Serializer.extend({ - - /** - Type of data to serialize. - @property dataType - @type String - @default 'json' - */ - dataType: 'json', - /** - Additional content type headers when transmitting data. - @property contentType - @type String - @default 'application/json; charset=utf-8' - */ - contentType: 'application/json; charset=utf-8', - - /** - Transforms json object into model - @method deserialize - @param {RESTless.Model} resource model resource - @param {Object} data json data - @return {RESTless.Model} - */ - deserialize: function(resource, data) { - if(!data) { return resource; } - - var key, prop, meta; - - // Check for wrapped object by resource name: { post: { id:1, name:'post 1' } } - // This is the default from ActiveRecord - key = this._keyForResource(resource); - if(data[key]) { - data = data[key]; - } - - // extract any meta info - meta = this.extractMeta(data); - if(meta) { - resource.set('meta', meta); - } - - // iterate over each json property and deserialze - Ember.beginPropertyChanges(resource); - for(prop in data) { - if (data.hasOwnProperty(prop)) { - this.deserializeProperty(resource, prop, data[prop]); - } - } - resource.setProperties({ isLoaded: true, isDirty: false }); - Ember.endPropertyChanges(resource); - return resource; - }, - - /** - Transforms json key/value into model property - @method deserializeProperty - @param {RESTless.Model} resource model resource - @param {Object} prop json data key - @param {Object} value json data value - */ - deserializeProperty: function(resource, prop, value) { - var attrName = this.attributeNameForKey(resource.constructor, prop), - fields = get(resource.constructor, 'fields'), - field = fields.get(attrName), type, klass; - - // If the json contains a key not defined on the model, don't attempt to set it. - if (!field) { return; } - - type = field.type; - klass = get(Ember.lookup, type); - - // If property is a hasMany relationship, deserialze the array - if (field.hasMany) { - var hasManyArr = this.deserializeMany(resource.get(attrName), type, value); - resource.set(attrName, hasManyArr); - } - // If property is a belongsTo relationship, deserialze that model - else if (field.belongsTo && klass && value) { - var belongsToModel = klass.create({ isNew: false, isLoaded: true }).deserialize(value); - resource.set(attrName, belongsToModel); - } - else { - // Check for a custom transform - if (type && RESTless.JSONTransforms[type]) { - value = RESTless.JSONTransforms[type].deserialize(value); - } - resource.set(attrName, value); - } - }, - - /** - Transforms json array into a record array - @method deserializeMany - @param {RESTless.RecordArray} recordArray RecordArray - @param {String} type records class name - @param {Object} data json data - @return {RESTless.RecordArray} - */ - deserializeMany: function(recordArray, type, data) { - if(!data) { return recordArray; } - - var klass = get(Ember.lookup, type), - arrayData = this._arrayDataForType(type, data), - meta, i, len, item, content; - - if(!arrayData) { return recordArray; } - - if(recordArray) { - recordArray.set('isLoaded', false); - recordArray.clear(); - } else { - recordArray = RESTless.RecordArray.createWithContent(); - } - - len = arrayData.length; - if(len) { - content = []; - for(i=0; i - App.Adapter.map('App.Post', { primaryKey: 'slug' }); - App.Adapter.map('App.Person', { lastName: { key: 'lastNameOfPerson' } }); - */ - map: function(modelKey, config) { - var modelMap = this.get('configurations.models'), - modelConfig = modelMap.get(modelKey), - newConfig = {}, - configKey, propertyKeys, modifiedPropKey; - - for(configKey in config) { - if(config.hasOwnProperty(configKey)) { - if(config[configKey].hasOwnProperty('key')) { - // If trying to set a custom property key - // Transform and merge into a custom 'propertyKeys' hash to make lookup faster when deserializing - propertyKeys = modelConfig && modelConfig.hasOwnProperty('propertyKeys') ? modelConfig.propertyKeys : {}; - modifiedPropKey = config[configKey].key; - propertyKeys[modifiedPropKey] = configKey; - newConfig.propertyKeys = propertyKeys; - } else { - newConfig[configKey] = config[configKey]; - } - modelConfig = modelConfig ? Ember.merge(modelConfig, newConfig) : newConfig; - } - } - modelMap.set(modelKey, modelConfig); - return this; - }, - - /** - Helper to pluralize a model resource names. - Checks custom configs or simply appends a 's'. - @method pluralize - @param {String} resourceName Name of model class - @return {String} plural name - */ - pluralize: function(resourceName) { - var plurals = this.get('configurations.plurals'); - return (plurals && plurals[resourceName]) || resourceName + 's'; - } -}); - -/** - The REST Adapter handles sending and fetching data to and from a REST API. - - @class RESTAdapter - @namespace RESTless - @extends RESTless.Adapter -*/ -RESTless.RESTAdapter = RESTless.Adapter.extend({ - /** - Serializer used to transform data. - @property serializer - @type RESTless.Serializer - @default RESTless.JSONSerializer - */ - serializer: RESTless.JSONSerializer.create(), - - /** - Host url of the REST API if on a different domain than the app. - @property host - @type String - @optional - @example 'http://api.example.com' - */ - host: Ember.computed.oneWay('url'), - /** - Deprecated. - @property url - @type String - @deprecated Use: `host` - */ - url: null, - - /** - API namespace endpoint path - @property namespace - @type String - @optional - @example 'api/v1' - */ - namespace: null, - - /** - If an API requires certain headers to be transmitted, e.g. an api key, - you can add a hash of headers to be sent on each request. - @property headers - @type Object - @optional - @example '{ "X-API-KEY" : "abc1234" }' - */ - headers: null, - /** - If an API requires paramters to be set on every request, - e.g. an api key, you can add a hash of defaults. - @property defaultData - @type Object - @optional - @example '{ api_key: "abc1234" }' - */ - defaultData: null, - - /** - Adds content type extensions to requests. - @property useContentTypeExtension - @type Boolean - @default false - @example - When `true` will make requests `/posts.json` instead of `/posts` or `/posts/115.json` instead of `/posts/115` - */ - useContentTypeExtension: false, - - /** - Computed path based on host and namespace. - @property rootPath - @type String - @final - */ - rootPath: Ember.computed(function() { - var a = document.createElement('a'), - host = this.get('host'), - ns = this.get('namespace'), - rootReset = ns && ns.charAt(0) === '/'; - - a.href = host ? host : '/'; - if(ns) { - a.pathname = rootReset ? ns : (a.pathname + ns); - } - return a.href.replace(/\/+$/, ''); - }).property('host', 'namespace'), - - /** - Helper method creates a valid REST path to a resource - @method resourcePath - @param {String} resourceName Type of Model - @return {String} the resource path - @example App.Post => 'posts', App.PostGroup => 'post_groups' - */ - resourcePath: function(resourceName) { - return this.pluralize(Ember.String.decamelize(resourceName)); - }, - - /** - Creates and executes an ajax request wrapped in a promise. - @method request - @param {RESTless.Model} model model to use to build the request - @param {Object} [params] Additional ajax params - @param {Object} [key] optional resource primary key value - @return {Ember.RSVP.Promise} - */ - request: function(model, params, key) { - var adapter = this, - ajaxParams = this.prepareParams(params); - ajaxParams.url = this.buildUrl(model, key); - - return new Ember.RSVP.Promise(function(resolve, reject) { - ajaxParams.success = function(data) { - Ember.run(null, resolve, data); - }; - ajaxParams.error = function(jqXHR, textStatus, errorThrown) { - var errors = adapter.parseAjaxErrors(jqXHR, textStatus, errorThrown); - Ember.run(null, reject, errors); - }; - - var ajax = Ember.$.ajax(ajaxParams); - // (private) store current ajax request on the model. - model.currentRequest = ajax; - }); - }, - - /** - Builds ajax request parameters - @method prepareParams - @param {Object} [params] base ajax params - @return {Object} - @private - */ - prepareParams: function(params) { - var serializer = this.serializer, - headers = this.get('headers'), - defaultData = this.get('defaultData'); - params = params || {}; - params.dataType = serializer.dataType; - params.contentType = serializer.contentType; - if(headers) { - params.headers = headers; - } - if(defaultData) { - params.data = $.extend({}, defaultData, params.data); - } - if(params.data && params.type !== 'GET') { - params.data = serializer.prepareData(params.data); - } - return params; - }, - - /** - Constructs request url and dynamically adds the resource key if specified - @method buildUrl - @private - */ - buildUrl: function(model, key) { - var resourcePath = this.resourcePath(get(model.constructor, 'resourceName')), - primaryKey = get(model.constructor, 'primaryKey'), - urlParts = [this.get('rootPath'), resourcePath], - dataType, url; - - if(key) { - urlParts.push(key); - } else if(model.get(primaryKey)) { - urlParts.push(model.get(primaryKey)); - } - url = urlParts.join('/'); - - if(this.useContentTypeExtension) { - dataType = this.serializer.dataType; - if(dataType) { - url += '.' + dataType; - } - } - return url; - }, - - /** - Saves a record. POSTs a new record, or PUTs an updated record to REST API - @method saveRecord - @param {RESTless.Model} record record to be saved - @return {Ember.RSVP.Promise} - */ - saveRecord: function(record) { - var isNew = record.get('isNew'), method, ajaxPromise; - //If an existing model isn't dirty, no need to save. - if(!isNew && !record.get('isDirty')) { - return new Ember.RSVP.Promise(function(resolve, reject){ - resolve(record); - }); - } - - record.set('isSaving', true); - method = isNew ? 'POST' : 'PUT'; - ajaxPromise = this.request(record, { type: method, data: record.serialize() }); - - ajaxPromise.then(function(data){ - if(data) { - record.deserialize(data); - } - record.onSaved(isNew); - return record; - }, function(error) { - record.onError(error); - return error; - }); - - return ajaxPromise; - }, - - /** - Deletes a record from REST API using DELETE - @method deleteRecord - @param {RESTless.Model} record record to be deleted - @return {Ember.RSVP.Promise} - */ - deleteRecord: function(record) { - var ajaxPromise = this.request(record, { type: 'DELETE', data: record.serialize() }); - - ajaxPromise.then(function() { - record.onDeleted(); - return null; - }, function(error) { - record.onError(error); - return error; - }); - - return ajaxPromise; - }, - - /** - Reloads a record from REST API - @method reloadRecord - @param {RESTless.Model} record record to be reloaded - @return {Ember.RSVP.Promise} - */ - reloadRecord: function(record) { - var klass = record.constructor, - primaryKey = get(klass, 'primaryKey'), - key = record.get(primaryKey), ajaxPromise; - - // Can't reload a record that hasn't been stored yet (no primary key) - if(Ember.isNone(key)) { - return new Ember.RSVP.Promise(function(resolve, reject){ - reject(null); - }); - } - - record.set('isLoaded', false); - ajaxPromise = this.request(record, { type: 'GET' }, key); - ajaxPromise.then(function(data){ - record.deserialize(data); - record.onLoaded(); - }, function(error) { - record.onError(error); - }); - - return ajaxPromise; - }, - - /** - Finds all records of specified class using GET - @method findAll - @param {RESTless.Model} klass model type to find - @return {RESTless.RecordArray} - */ - findAll: function(klass) { - return this.findQuery(klass); - }, - - /** - Finds records with specified query params using GET - @method findQuery - @param {RESTless.Model} klass model type to find - @param {Object} queryParams hash of query params - @return {RESTless.RecordArray} - */ - findQuery: function(klass, queryParams) { - var type = klass.toString(), - resourceInstance = klass.create({ isNew: false }), - result = RESTless.RecordArray.createWithContent(), - ajaxPromise = this.request(resourceInstance, { type: 'GET', data: queryParams }); - - ajaxPromise.then(function(data){ - result.deserializeMany(type, data); - result.onLoaded(); - }, function(error) { - result.onError(error); - }); - - return result; - }, - - /** - Finds record with specified primary key using GET - @method findByKey - @param {RESTless.Model} klass model type to find - @param {Number|String} key primary key value - @param {Object} [queryParams] hash of additional query params - @return {RESTless.Model} - */ - findByKey: function(klass, key, queryParams) { - var result = klass.create({ isNew: false }), - ajaxPromise = this.request(result, { type: 'GET', data: queryParams }, key); - - ajaxPromise.then(function(data){ - result.deserialize(data); - result.onLoaded(); - }, function(error) { - result.onError(error); - }); - - return result; - }, - - /** - Registers custom attribute transforms. - Fowards creation to serializer. - @method registerTransform - */ - registerTransform: function(type, transform) { - this.get('serializer').registerTransform(type, transform); - }, - - /** - Builds a robust error object using the serializer and xhr data - @method parseAjaxErrors - @private - */ - parseAjaxErrors: function(jqXHR, textStatus, errorThrown) { - // use serializer to parse error messages from server - var errors = this.get('serializer').parseError(jqXHR.responseText) || {}; - // add additional xhr error info - errors.status = jqXHR.status; - errors.state = jqXHR.state(); - errors.textStatus = textStatus; - errors.errorThrown = errorThrown; - return errors; - } -}); - -/** - The Client is the top level store, housing the default adapter and configurations. - The client will be automatically detected and set from your App namespace. - Setting a client is optional and will automatically use a base client. - - @class Client - @namespace RESTless - @extends Ember.Object -*/ -RESTless.Client = Ember.Object.extend({ - /** - The default adapter for all models. - @property adapter - @type RESTless.Adapter - */ - adapter: RESTless.RESTAdapter.create(), - /** - Shortcut alias to model configurations - @private - */ - _modelConfigs: Ember.computed.alias('adapter.configurations.models') -}); - -Ember.onLoad('Ember.Application', function(Application) { - Application.initializer({ - name: 'Client', - initialize: function(container, application) { - var client = application.Client ? application.Client : RESTless.Client.create(); - RESTless.set('client', client); - application.addObserver('Client', this, function() { - RESTless.set('client', this.Client); - }); - } - }); -}); - -/** - The State Mixin is responsible for keeping state and - handling state events for Models. - - @class State - @namespace RESTless - @uses Ember.Evented -*/ -RESTless.State = Ember.Mixin.create( Ember.Evented, { - /** - Model has not yet been saved; not yet assigned a primary key. - @property isNew - @type {Boolean} - */ - isNew: true, - /** - Model has been retrieved and properties set. - @property isLoaded - @type {Boolean} - */ - isLoaded: false, - /** - Model has changes that have not yet been saved. - @property isDirty - @type {Boolean} - */ - isDirty: false, - /** - Model model is in the process of saving. - @property isSaving - @type {Boolean} - */ - isSaving: false, - /** - Model model is in error state. - @property isError - @type {Boolean} - */ - isError: false, - - /** - Hash of current errors on model. - @property errors - @type Object - */ - errors: null, - - /** - Fired when the record is created. - @event didCreate - */ - didCreate: Ember.K, - /** - Fired when the record is updated. - @event didUpdate - */ - didUpdate: Ember.K, - /** - Fired when the record is enters the loaded state. - @event didLoad - */ - didLoad: Ember.K, - /** - Fired when the record is deleted. - @event didDelete - */ - didDelete: Ember.K, - /** - Fired when the record enters the error state. - @event becameError - */ - becameError: Ember.K, - - /** - Updates state and triggers events upon saving. - @method onSaved - @param {Boolean} wasNew was a new model prior to saving. - */ - onSaved: function(wasNew) { - this.setProperties({ - isDirty: false, - isSaving: false, - isLoaded: true, - isError: false, - errors: null - }); - this._triggerEvent(wasNew ? 'didCreate' : 'didUpdate', this); - this._triggerEvent('didLoad', this); - }, - - /** - Updates state and triggers events upon deletion. - @method onDeleted - */ - onDeleted: function() { - this._triggerEvent('didDelete', this); - Ember.run.next(this, function() { - this.destroy(); - }); - }, - - /** - Updates state and triggers events upon loading. - @method onLoaded - */ - onLoaded: function() { - this.setProperties({ - isDirty: false, - isSaving: false, - isLoaded: true, - isError: false, - errors: null - }); - this._triggerEvent('didLoad', this); - }, - - /** - Updates state and triggers events upon an error. - @method onError - */ - onError: function(errors) { - this.setProperties({ - isSaving: false, - isError: true, - errors: errors - }); - this._triggerEvent('becameError', errors); - }, - - /** - Clears errors and resets error state - @method clearErrors - @returns {Object} - */ - clearErrors: function() { - this.setProperties({ isError: false, errors: null }); - return this; - }, - - /** - Copies the current state to a cloned object - @method copyState - @param {Object} clone the cloned object - @returns {Object} the cloned object with copied state - */ - copyState: function(clone) { - var mi = RESTless.State.mixins, - props = mi[mi.length-1].properties; - Ember.beginPropertyChanges(clone); - for(var p in props) { - if(props.hasOwnProperty(p) && typeof props[p] !== 'function') { - clone.set(p, this.get(p)); - } - } - Ember.endPropertyChanges(clone); - return clone; - }, - - /** - Flag for deferring dirty state when setting initial values on create() or load() - @property _isReady - @type {Boolean} - @private - */ - _isReady: false, - - /** - Helper function to trigger events on models and to any listeners. - @method _triggerEvent - @private - */ - _triggerEvent: function(event, data) { - Ember.run(this, function() { - Ember.tryInvoke(this, event, [data]); - this.trigger(event, data); - }); - } -}); - -/** - The base model class for all RESTless objects. - - @class Model - @namespace RESTless - @extends Ember.Object - @uses RESTless.State - @uses Ember.Copyable -*/ -RESTless.Model = Ember.Object.extend( RESTless.State, Ember.Copyable, { - /** - A unique id number for the record. `id` is the default primary key. - @property id - */ - id: RESTless.attr('number'), - - /** - Stores raw model data. Don't use directly; use declared model attributes. - @private - */ - __data: null, - _data: Ember.computed(function() { - if (!this.__data) { this.__data = {}; } - return this.__data; - }), - - /** - Hook to add observers for each attribute/relationship for 'isDirty' functionality - @protected - */ - didDefineProperty: function(proto, key, value) { - if (value instanceof Ember.Descriptor) { - var meta = value.meta(); - - if (meta.isRelationship && !meta.readOnly) { - // If a relationship's property becomes dirty, need to mark owner as dirty. - Ember.addObserver(proto, key + '.isDirty', null, '_onRelationshipChange'); - } - } - }, - - /** - _onPropertyChange: called when any property of the model changes - If the model has been loaded, or is new, isDirty flag is set to true. - @private - */ - _onPropertyChange: function(key) { - var isNew = this.get('isNew'); - - // No longer a new record once a primary key is assigned. - if (isNew && get(this.constructor, 'primaryKey') === key) { - this.set('isNew', false); - isNew = false; - } - - if (this.get('_isReady') && (isNew || this.get('isLoaded'))) { - this.set('isDirty', true); - } - }, - /** - Called when a relationship property's isDirty state changes. - Forwards a _onPropertyChange event for the parent object. - @private - */ - _onRelationshipChange: function(sender, key) { - if(sender.get(key)) { // if isDirty - this._onPropertyChange(key); - } - }, - - /** - Creates a clone of the model. Implements Ember.Copyable protocol - - @method copy - @param {Boolean} deep if `true`, a deep copy of the object should be made - @return {Object} copy of receiver - */ - copy: function(deep) { - var clone = this.constructor.create(), - fields = get(this.constructor, 'fields'); - - Ember.beginPropertyChanges(this); - fields.forEach(function(field, opts) { - var value = this.get(field); - if (value !== null) { - clone.set(field, value); - } - }, this); - Ember.endPropertyChanges(this); - - return clone; - }, - - /** - Creates a clone copy of the model along with it's current State. - @method copyWithState - @param {Boolean} deep if `true`, a deep copy of the object should be made - @return {Object} copy of receiver - */ - copyWithState: function(deep) { - return this.copyState(this.copy(deep)); - }, - - /** - Saves the record using the model's adapter. - @method saveRecord - @chainable - */ - saveRecord: function() { - return get(this.constructor, 'adapter').saveRecord(this); - }, - /** - Deletes the record using the model's adapter. - @method deleteRecord - @chainable - */ - deleteRecord: function() { - return get(this.constructor, 'adapter').deleteRecord(this); - }, - /** - Reloads the record using the model's adapter. - @method reloadRecord - @chainable - */ - reloadRecord: function() { - return get(this.constructor, 'adapter').reloadRecord(this); - }, - - /** - Serializes the record into its data representaion. - @method serialize - @param {Object} options hash of serialization options - @chainable - */ - serialize: function(options) { - return RESTless.get('client.adapter.serializer').serialize(this, options); - }, - /** - Deserializes raw data into Model properties - @method deserialize - @param {Object} data raw data to deserialize - @chainable - */ - deserialize: function(data) { - return RESTless.get('client.adapter.serializer').deserialize(this, data); - }, - /** - Serializes a Model property into its data representaion. - @method serializeProperty - @param {String} prop property key - @chainable - */ - serializeProperty: function(prop) { - return RESTless.get('client.adapter.serializer').serializeProperty(this, prop); - }, - /** - Deserializes raw data property into Model property - @method deserializeProperty - @param {String} prop property key - @param value property value - @chainable - */ - deserializeProperty: function(prop, value) { - return RESTless.get('client.adapter.serializer').deserializeProperty(this, prop, value); - } -}); - -/** - Static properties and methods for the Model Class. - - @class Model - @namespace RESTless -*/ -RESTless.Model.reopenClass({ - /** - Extends super class `create` and marks _isReady state. - @method create - @return RESTless.Model - */ - create: function() { - var instance = this._super.apply(this, arguments); - instance.set('_isReady', true); - return instance; - }, - /** - Alias to `create`. Eases transition to/from ember-data - @deprecated Use `create` - @method createRecord - @return RESTless.Model - */ - createRecord: Ember.aliasMethod('create'), - - /** - The adapter for the Model. Provides a hook for overriding. - @property adapter - @type RESTless.Adapter - */ - adapter: Ember.computed(function() { - return get(RESTless, 'client.adapter'); - }).property('RESTless.client.adapter'), - - /** - The property name for the primary key - @property primaryKey - @type String - @default 'id' - */ - primaryKey: Ember.computed(function() { - var className = this.toString(), - modelConfig = get(RESTless, 'client._modelConfigs').get(className); - if(modelConfig && modelConfig.primaryKey) { - return modelConfig.primaryKey; - } - return 'id'; - }).property('RESTless.client._modelConfigs'), - - /** - The name of the resource, derived from the class name. - App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' - - @property resourceName - @type String - */ - resourceName: Ember.computed(function() { - var classNameParts = this.toString().split('.'); - return classNameParts[classNameParts.length-1]; - }), - /** - The plural name of the resource, derived from the class name. - App.Post => 'Post', App.PostGroup => 'PostGroup', App.AnotherNamespace.Post => 'Post' - - @property resourceNamePlural - @type String - */ - resourceNamePlural: Ember.computed(function() { - var resourceName = get(this, 'resourceName'), - adapter = get(this, 'adapter'); - return adapter.pluralize(Ember.String.decamelize(resourceName)); - }), - - /** - Meta information for all attributes and relationships - @property fields - @type Ember.Map - */ - fields: Ember.computed(function() { - var map = Ember.Map.create(); - this.eachComputedProperty(function(name, meta) { - if (meta.isAttribute || meta.isRelationship) { - map.set(name, meta); - } - }); - return map; - }), - - /** - Find resources using the adapter. - This method can handle all find types: `findAll`, `findQuery`, `findByKey` - @method find - @param {Object} params - @return Object - */ - find: function(params) { - return get(this, 'adapter').find(this, params); - }, - /** - Finds resources using the adapter, and returns a promise. - @method fetch - @param {Object} params hash of query params - @return Ember.RSVP.Promise - */ - fetch: function(params) { - return get(this, 'adapter').fetch(this, params); - }, - /** - Finds all resources of this type using the adapter. - @method findAll - @return Object - */ - findAll: function() { - return get(this, 'adapter').findAll(this); - }, - /** - Find resources with query using the adapter. - @method findQuery - @param {Object} params hash of query params - @return Object - */ - findQuery: function(params) { - return get(this, 'adapter').findQuery(this, params); - }, - /** - Find resource with specified primary key using the adapter. - @method findByKey - @param {Number|String} key - @param {Object} params any additional params - @return Object - */ - findByKey: function(key, params) { - return get(this, 'adapter').findByKey(this, key, params); - }, - /** - Find resource with specified id using the adapter. - Keeps API similar to ember-data. - @method findById - @deprecated Use `findByKey` - */ - findById: Ember.aliasMethod('findByKey'), - - /** - Create model directly from data representation. - @method load - @param {Object} data raw data to load - @return RESTless.Model - */ - load: function(data) { - var model = this.create().set('_isReady', false).deserialize(data).set('_isReady', true); - model.onLoaded(); - return model; - }, - /** - Create collection of records directly from data representation.. - @method loadMany - @param {Object} data raw data to load - @return RESTless.RecordArray - */ - loadMany: function(data) { - var array = RESTless.RecordArray.createWithContent().deserializeMany(this.toString(), data); - array.onLoaded(); - return array; - } -}); - -/** - A read-only model. Removes property change observers and write methods. - Helps improve performance when write functionality is not needed. - - @class ReadOnlyModel - @namespace RESTless - @extends RESTless.Model -*/ -RESTless.ReadOnlyModel = RESTless.Model.extend({ - serialize: null, - saveRecord: null, - deleteRecord: null, - didDefineProperty: null, - _onPropertyChange: Ember.K -}); - -/** - RecordArray is an Array of Model objects. - - @class RecordArray - @namespace RESTless - @extends Ember.ArrayProxy - @uses RESTless.State -*/ -RESTless.RecordArray = Ember.ArrayProxy.extend( RESTless.State, { - /** - The default adapter for the RecordArray. Providing a hook for overriding. - @property adapter - */ - adapter: Ember.computed(function() { - return get(RESTless, 'client.adapter'); - }).property('RESTless.client.adapter'), - - /** - Use the current Serializer to turn the data into a record array. - @method deserializeMany - @param {Object} type The type of model class - @param {Object} data The data to deserialize - @returns RESTless.RecordArray - */ - deserializeMany: function(type, data) { - return get(this, 'adapter.serializer').deserializeMany(this, type, data); - }, - - /** - Use the current Serializer to turn the array into its data representation. - @method serializeMany - @param {Object} type The type of model class - @returns RESTless.RecordArray - */ - serializeMany: function(type) { - return get(this, 'adapter.serializer').serializeMany(this, type); - }, - - /** - Overrides super replaceContent method to add isDirty functionality - @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. - */ - replaceContent: function(idx, amt, objects) { - get(this, 'content').replace(idx, amt, objects); - if (this.get('isLoaded')) { - this.set('isDirty', true); - } - }, - - /** - Observes when items become dirty and sets itself to dirty. - @private - */ - _onItemDirtyChange: Ember.observer(function() { - var clean = this.get('content').everyBy('isDirty', false); - if(this.get('isLoaded') && !clean) { - this.set('isDirty', true); - } - }, '@each.isDirty'), - - /** - Observes when the array's isLoaded state changes and triggers each item's onLoaded. - @private - */ - _onLoadedChange: Ember.observer(function() { - if(this.get('isLoaded')) { - this.forEach(function(item) { - if(RESTless.Model.detectInstance(item)) { - item.onLoaded(); - } - }); - } - }, 'isLoaded') -}); - - -RESTless.RecordArray.reopenClass({ - /** - Creates a RecordArray - @method create - @returns RESTless.RecordArray - */ - create: function() { - var arr = this._super.apply(this, arguments); - // override State defaults not implemented or applicable to arrays - arr.setProperties({ _isReady: true, isNew: false }); - return arr; - }, - /** - Helper to create a RecordArray with it's content property initialized to an Array - @method createWithContent - @returns RESTless.RecordArray - */ - createWithContent: function() { - var arr = this.create.apply(this, arguments); - if(!arr.content) { arr.set('content', Ember.A()); } - return arr; - } -}); - -/* - Date.parse with progressive enhancement for ISO 8601 - © 2011 Colin Snover - Released under MIT license. - Copied from: -*/ -Ember.Date = Ember.Date || {}; - -var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; - -Ember.Date.parse = function (date) { - var timestamp, struct, minutesOffset = 0; - - // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string - // before falling back to any implementation-specific date parsing, so that’s what we do, even if native - // implementations could be faster - // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm - if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { - // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC - for (var i = 0, k; (k = numericKeys[i]); ++i) { - struct[k] = +struct[k] || 0; - } - - // allow undefined days and months - struct[2] = (+struct[2] || 1) - 1; - struct[3] = +struct[3] || 1; - - if (struct[8] !== 'Z' && struct[9] !== undefined) { - minutesOffset = struct[10] * 60 + struct[11]; - - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; - } - } - - timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); - } - else { - timestamp = origParse ? origParse(date) : NaN; - } - - return timestamp; -}; - -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { - Date.parse = Ember.Date.parse; -} - -/** - * From ember-data: - * https://github.com/emberjs/data/blob/master/packages/ember-data/lib/transforms/json_transforms.js - */ - -var isNone = Ember.isNone, isEmpty = Ember.isEmpty; - -/** - @class JSONTransforms - @static - @namespace RESTless -*/ -RESTless.JSONTransforms = { - string: { - deserialize: function(serialized) { - return isNone(serialized) ? null : String(serialized); - }, - - serialize: function(deserialized) { - return isNone(deserialized) ? null : String(deserialized); - } - }, - - number: { - deserialize: function(serialized) { - return isEmpty(serialized) ? null : Number(serialized); - }, - - serialize: function(deserialized) { - return isEmpty(deserialized) ? null : Number(deserialized); - } - }, - - // Handles the following boolean inputs: - // "TrUe", "t", "f", "FALSE", 0, (non-zero), or boolean true/false - 'boolean': { - deserialize: function(serialized) { - var type = typeof serialized; - - if (type === "boolean") { - return serialized; - } else if (type === "string") { - return serialized.match(/^true$|^t$|^1$/i) !== null; - } else if (type === "number") { - return serialized === 1; - } else { - return false; - } - }, - - serialize: function(deserialized) { - return Boolean(deserialized); - } - }, - - date: { - deserialize: function(serialized) { - var type = typeof serialized; - - if (type === "string") { - return new Date(Ember.Date.parse(serialized)); - } else if (type === "number") { - return new Date(serialized); - } else if (serialized === null || serialized === undefined) { - // if the value is not present in the data, - // return undefined, not null. - return serialized; - } else { - return null; - } - }, - - serialize: function(date) { - if (date instanceof Date) { - var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - - var pad = function(num) { - return num < 10 ? "0"+num : ""+num; - }; - - var utcYear = date.getUTCFullYear(), - utcMonth = date.getUTCMonth(), - utcDayOfMonth = date.getUTCDate(), - utcDay = date.getUTCDay(), - utcHours = date.getUTCHours(), - utcMinutes = date.getUTCMinutes(), - utcSeconds = date.getUTCSeconds(); - - - var dayOfWeek = days[utcDay]; - var dayOfMonth = pad(utcDayOfMonth); - var month = months[utcMonth]; - - return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " + - pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT"; - } else { - return null; - } - } - } -}; - -})(this, jQuery, Ember); \ No newline at end of file From 2a8649706a6a3815d4620f566c7f08e3bc81f34f Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 5 Mar 2014 18:32:32 +0530 Subject: [PATCH 17/68] Add welcome command file --- web/clira/system/welcome.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 web/clira/system/welcome.js diff --git a/web/clira/system/welcome.js b/web/clira/system/welcome.js new file mode 100644 index 0000000..d136585 --- /dev/null +++ b/web/clira/system/welcome.js @@ -0,0 +1,24 @@ +/* + * Copyright 2014, Juniper Network Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../../../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + */ + +jQuery(function($) { + jQuery.clira.commandFile({ + name: "welcome", + templatesFile: '/clira/templates/welcome.hbs', + commands: [ + { + command: "show welcome screen", + help: "Displays start up screen", + templateName: "show-welcome", + execute: function (view, cmd, parse, poss) { + } + } + ] + }); +}); From bbbb2df0581cc0d03254534c4624d380feac4fde Mon Sep 17 00:00:00 2001 From: ajhai Date: Wed, 5 Mar 2014 18:36:08 +0530 Subject: [PATCH 18/68] Update jQuery-ui to 1.10.4 --- web/external/jquery-ui/jquery-ui-1.10.2.js | 14987 ------------------- web/external/jquery-ui/jquery-ui.js | 7976 +++++----- web/external/jquery-ui/jquery-ui.min.js | 13 +- 3 files changed, 4062 insertions(+), 18914 deletions(-) delete mode 100644 web/external/jquery-ui/jquery-ui-1.10.2.js diff --git a/web/external/jquery-ui/jquery-ui-1.10.2.js b/web/external/jquery-ui/jquery-ui-1.10.2.js deleted file mode 100644 index 6eccbfe..0000000 --- a/web/external/jquery-ui/jquery-ui-1.10.2.js +++ /dev/null @@ -1,14987 +0,0 @@ -/*! jQuery UI - v1.10.2 - 2013-03-14 -* http://jqueryui.com -* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.position.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js -* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ -(function( $, undefined ) { - -var uuid = 0, - runiqueId = /^ui-id-\d+$/; - -// $.ui might exist from components with no dependencies, e.g., $.ui.position -$.ui = $.ui || {}; - -$.extend( $.ui, { - version: "1.10.2", - - keyCode: { - BACKSPACE: 8, - COMMA: 188, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - LEFT: 37, - NUMPAD_ADD: 107, - NUMPAD_DECIMAL: 110, - NUMPAD_DIVIDE: 111, - NUMPAD_ENTER: 108, - NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SPACE: 32, - TAB: 9, - UP: 38 - } -}); - -// plugins -$.fn.extend({ - focus: (function( orig ) { - return function( delay, fn ) { - return typeof delay === "number" ? - this.each(function() { - var elem = this; - setTimeout(function() { - $( elem ).focus(); - if ( fn ) { - fn.call( elem ); - } - }, delay ); - }) : - orig.apply( this, arguments ); - }; - })( $.fn.focus ), - - scrollParent: function() { - var scrollParent; - if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { - scrollParent = this.parents().filter(function() { - return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); - }).eq(0); - } else { - scrollParent = this.parents().filter(function() { - return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); - }).eq(0); - } - - return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; - }, - - zIndex: function( zIndex ) { - if ( zIndex !== undefined ) { - return this.css( "zIndex", zIndex ); - } - - if ( this.length ) { - var elem = $( this[ 0 ] ), position, value; - while ( elem.length && elem[ 0 ] !== document ) { - // Ignore z-index if position is set to a value where z-index is ignored by the browser - // This makes behavior of this function consistent across browsers - // WebKit always returns auto if the element is positioned - position = elem.css( "position" ); - if ( position === "absolute" || position === "relative" || position === "fixed" ) { - // IE returns 0 when zIndex is not specified - // other browsers return a string - // we ignore the case of nested elements with an explicit value of 0 - //
- value = parseInt( elem.css( "zIndex" ), 10 ); - if ( !isNaN( value ) && value !== 0 ) { - return value; - } - } - elem = elem.parent(); - } - } - - return 0; - }, - - uniqueId: function() { - return this.each(function() { - if ( !this.id ) { - this.id = "ui-id-" + (++uuid); - } - }); - }, - - removeUniqueId: function() { - return this.each(function() { - if ( runiqueId.test( this.id ) ) { - $( this ).removeAttr( "id" ); - } - }); - } -}); - -// selectors -function focusable( element, isTabIndexNotNaN ) { - var map, mapName, img, - nodeName = element.nodeName.toLowerCase(); - if ( "area" === nodeName ) { - map = element.parentNode; - mapName = map.name; - if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { - return false; - } - img = $( "img[usemap=#" + mapName + "]" )[0]; - return !!img && visible( img ); - } - return ( /input|select|textarea|button|object/.test( nodeName ) ? - !element.disabled : - "a" === nodeName ? - element.href || isTabIndexNotNaN : - isTabIndexNotNaN) && - // the element and all of its ancestors must be visible - visible( element ); -} - -function visible( element ) { - return $.expr.filters.visible( element ) && - !$( element ).parents().addBack().filter(function() { - return $.css( this, "visibility" ) === "hidden"; - }).length; -} - -$.extend( $.expr[ ":" ], { - data: $.expr.createPseudo ? - $.expr.createPseudo(function( dataName ) { - return function( elem ) { - return !!$.data( elem, dataName ); - }; - }) : - // support: jQuery <1.8 - function( elem, i, match ) { - return !!$.data( elem, match[ 3 ] ); - }, - - focusable: function( element ) { - return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); - }, - - tabbable: function( element ) { - var tabIndex = $.attr( element, "tabindex" ), - isTabIndexNaN = isNaN( tabIndex ); - return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); - } -}); - -// support: jQuery <1.8 -if ( !$( "" ).outerWidth( 1 ).jquery ) { - $.each( [ "Width", "Height" ], function( i, name ) { - var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], - type = name.toLowerCase(), - orig = { - innerWidth: $.fn.innerWidth, - innerHeight: $.fn.innerHeight, - outerWidth: $.fn.outerWidth, - outerHeight: $.fn.outerHeight - }; - - function reduce( elem, size, border, margin ) { - $.each( side, function() { - size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; - if ( border ) { - size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; - } - if ( margin ) { - size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; - } - }); - return size; - } - - $.fn[ "inner" + name ] = function( size ) { - if ( size === undefined ) { - return orig[ "inner" + name ].call( this ); - } - - return this.each(function() { - $( this ).css( type, reduce( this, size ) + "px" ); - }); - }; - - $.fn[ "outer" + name] = function( size, margin ) { - if ( typeof size !== "number" ) { - return orig[ "outer" + name ].call( this, size ); - } - - return this.each(function() { - $( this).css( type, reduce( this, size, true, margin ) + "px" ); - }); - }; - }); -} - -// support: jQuery <1.8 -if ( !$.fn.addBack ) { - $.fn.addBack = function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - }; -} - -// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) -if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { - $.fn.removeData = (function( removeData ) { - return function( key ) { - if ( arguments.length ) { - return removeData.call( this, $.camelCase( key ) ); - } else { - return removeData.call( this ); - } - }; - })( $.fn.removeData ); -} - - - - - -// deprecated -$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); - -$.support.selectstart = "onselectstart" in document.createElement( "div" ); -$.fn.extend({ - disableSelection: function() { - return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + - ".ui-disableSelection", function( event ) { - event.preventDefault(); - }); - }, - - enableSelection: function() { - return this.unbind( ".ui-disableSelection" ); - } -}); - -$.extend( $.ui, { - // $.ui.plugin is deprecated. Use the proxy pattern instead. - plugin: { - add: function( module, option, set ) { - var i, - proto = $.ui[ module ].prototype; - for ( i in set ) { - proto.plugins[ i ] = proto.plugins[ i ] || []; - proto.plugins[ i ].push( [ option, set[ i ] ] ); - } - }, - call: function( instance, name, args ) { - var i, - set = instance.plugins[ name ]; - if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { - return; - } - - for ( i = 0; i < set.length; i++ ) { - if ( instance.options[ set[ i ][ 0 ] ] ) { - set[ i ][ 1 ].apply( instance.element, args ); - } - } - } - }, - - // only used by resizable - hasScroll: function( el, a ) { - - //If overflow is hidden, the element might have extra content, but the user wants to hide it - if ( $( el ).css( "overflow" ) === "hidden") { - return false; - } - - var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", - has = false; - - if ( el[ scroll ] > 0 ) { - return true; - } - - // TODO: determine which cases actually cause this to happen - // if the element doesn't have the scroll set, see if it's possible to - // set the scroll - el[ scroll ] = 1; - has = ( el[ scroll ] > 0 ); - el[ scroll ] = 0; - return has; - } -}); - -})( jQuery ); - -(function( $, undefined ) { - -var uuid = 0, - slice = Array.prototype.slice, - _cleanData = $.cleanData; -$.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); -}; - -$.widget = function( name, base, prototype ) { - var fullName, existingConstructor, constructor, basePrototype, - // proxiedPrototype allows the provided prototype to remain unmodified - // so that it can be used as a mixin for multiple widgets (#8876) - proxiedPrototype = {}, - namespace = name.split( "." )[ 0 ]; - - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { - return !!$.data( elem, fullName ); - }; - - $[ namespace ] = $[ namespace ] || {}; - existingConstructor = $[ namespace ][ name ]; - constructor = $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without "new" keyword - if ( !this._createWidget ) { - return new constructor( options, element ); - } - - // allow instantiation without initializing for simple inheritance - // must use "new" keyword (the code above always passes args) - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - // extend with the existing constructor to carry over any static properties - $.extend( constructor, existingConstructor, { - version: prototype.version, - // copy the object used to create the prototype in case we need to - // redefine the widget later - _proto: $.extend( {}, prototype ), - // track widgets that inherit from this widget in case this widget is - // redefined after a widget inherits from it - _childConstructors: [] - }); - - basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from - basePrototype.options = $.widget.extend( {}, basePrototype.options ); - $.each( prototype, function( prop, value ) { - if ( !$.isFunction( value ) ) { - proxiedPrototype[ prop ] = value; - return; - } - proxiedPrototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); - }); - constructor.prototype = $.widget.extend( basePrototype, { - // TODO: remove support for widgetEventPrefix - // always use the name + a colon as the prefix, e.g., draggable:start - // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name - }, proxiedPrototype, { - constructor: constructor, - namespace: namespace, - widgetName: name, - widgetFullName: fullName - }); - - // If this widget is being redefined then we need to find all widgets that - // are inheriting from it and redefine all of them so that they inherit from - // the new version of this widget. We're essentially trying to replace one - // level in the prototype chain. - if ( existingConstructor ) { - $.each( existingConstructor._childConstructors, function( i, child ) { - var childPrototype = child.prototype; - - // redefine the child widget using the same prototype that was - // originally used, but inherit from the new version of the base - $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); - }); - // remove the list of existing child constructors from the old constructor - // so the old child constructors can be garbage collected - delete existingConstructor._childConstructors; - } else { - base._childConstructors.push( constructor ); - } - - $.widget.bridge( name, constructor ); -}; - -$.widget.extend = function( target ) { - var input = slice.call( arguments, 1 ), - inputIndex = 0, - inputLength = input.length, - key, - value; - for ( ; inputIndex < inputLength; inputIndex++ ) { - for ( key in input[ inputIndex ] ) { - value = input[ inputIndex ][ key ]; - if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { - // Clone objects - if ( $.isPlainObject( value ) ) { - target[ key ] = $.isPlainObject( target[ key ] ) ? - $.widget.extend( {}, target[ key ], value ) : - // Don't extend strings, arrays, etc. with objects - $.widget.extend( {}, value ); - // Copy everything else by reference - } else { - target[ key ] = value; - } - } - } - } - return target; -}; - -$.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName || name; - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = slice.call( arguments, 1 ), - returnValue = this; - - // allow multiple hashes to be passed on init - options = !isMethodCall && args.length ? - $.widget.extend.apply( null, [ options ].concat(args) ) : - options; - - if ( isMethodCall ) { - this.each(function() { - var methodValue, - instance = $.data( this, fullName ); - if ( !instance ) { - return $.error( "cannot call methods on " + name + " prior to initialization; " + - "attempted to call method '" + options + "'" ); - } - if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { - return $.error( "no such method '" + options + "' for " + name + " widget instance" ); - } - methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue && methodValue.jquery ? - returnValue.pushStack( methodValue.get() ) : - methodValue; - return false; - } - }); - } else { - this.each(function() { - var instance = $.data( this, fullName ); - if ( instance ) { - instance.option( options || {} )._init(); - } else { - $.data( this, fullName, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( /* options, element */ ) {}; -$.Widget._childConstructors = []; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - defaultElement: "
", - options: { - disabled: false, - - // callbacks - create: null - }, - _createWidget: function( options, element ) { - element = $( element || this.defaultElement || this )[ 0 ]; - this.element = $( element ); - this.uuid = uuid++; - this.eventNamespace = "." + this.widgetName + this.uuid; - this.options = $.widget.extend( {}, - this.options, - this._getCreateOptions(), - options ); - - this.bindings = $(); - this.hoverable = $(); - this.focusable = $(); - - if ( element !== this ) { - $.data( element, this.widgetFullName, this ); - this._on( true, this.element, { - remove: function( event ) { - if ( event.target === element ) { - this.destroy(); - } - } - }); - this.document = $( element.style ? - // element within the document - element.ownerDocument : - // element is window or document - element.document || element ); - this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); - } - - this._create(); - this._trigger( "create", null, this._getCreateEventData() ); - this._init(); - }, - _getCreateOptions: $.noop, - _getCreateEventData: $.noop, - _create: $.noop, - _init: $.noop, - - destroy: function() { - this._destroy(); - // we can probably remove the unbind calls in 2.0 - // all event bindings should go through this._on() - this.element - .unbind( this.eventNamespace ) - // 1.9 BC for #7810 - // TODO remove dual storage - .removeData( this.widgetName ) - .removeData( this.widgetFullName ) - // support: jquery <1.6.3 - // http://bugs.jquery.com/ticket/9413 - .removeData( $.camelCase( this.widgetFullName ) ); - this.widget() - .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); - - // clean up events and states - this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - }, - _destroy: $.noop, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key, - parts, - curOption, - i; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.widget.extend( {}, this.options ); - } - - if ( typeof key === "string" ) { - // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } - options = {}; - parts = key.split( "." ); - key = parts.shift(); - if ( parts.length ) { - curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); - for ( i = 0; i < parts.length - 1; i++ ) { - curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; - curOption = curOption[ parts[ i ] ]; - } - key = parts.pop(); - if ( value === undefined ) { - return curOption[ key ] === undefined ? null : curOption[ key ]; - } - curOption[ key ] = value; - } else { - if ( value === undefined ) { - return this.options[ key ] === undefined ? null : this.options[ key ]; - } - options[ key ] = value; - } - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var key; - - for ( key in options ) { - this._setOption( key, options[ key ] ); - } - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - } - - return this; - }, - - enable: function() { - return this._setOption( "disabled", false ); - }, - disable: function() { - return this._setOption( "disabled", true ); - }, - - _on: function( suppressDisabledCheck, element, handlers ) { - var delegateElement, - instance = this; - - // no suppressDisabledCheck flag, shuffle arguments - if ( typeof suppressDisabledCheck !== "boolean" ) { - handlers = element; - element = suppressDisabledCheck; - suppressDisabledCheck = false; - } - - // no element argument, shuffle and use this.element - if ( !handlers ) { - handlers = element; - element = this.element; - delegateElement = this.widget(); - } else { - // accept selectors, DOM elements - element = delegateElement = $( element ); - this.bindings = this.bindings.add( element ); - } - - $.each( handlers, function( event, handler ) { - function handlerProxy() { - // allow widgets to customize the disabled handling - // - disabled as an array instead of boolean - // - disabled class as method for disabling individual parts - if ( !suppressDisabledCheck && - ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) ) { - return; - } - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - - // copy the guid so direct unbinding works - if ( typeof handler !== "string" ) { - handlerProxy.guid = handler.guid = - handler.guid || handlerProxy.guid || $.guid++; - } - - var match = event.match( /^(\w+)\s*(.*)$/ ), - eventName = match[1] + instance.eventNamespace, - selector = match[2]; - if ( selector ) { - delegateElement.delegate( selector, eventName, handlerProxy ); - } else { - element.bind( eventName, handlerProxy ); - } - }); - }, - - _off: function( element, eventName ) { - eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; - element.unbind( eventName ).undelegate( eventName ); - }, - - _delay: function( handler, delay ) { - function handlerProxy() { - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - var instance = this; - return setTimeout( handlerProxy, delay || 0 ); - }, - - _hoverable: function( element ) { - this.hoverable = this.hoverable.add( element ); - this._on( element, { - mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); - }, - mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); - } - }); - }, - - _focusable: function( element ) { - this.focusable = this.focusable.add( element ); - this._on( element, { - focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); - }, - focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); - } - }); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - return !( $.isFunction( callback ) && - callback.apply( this.element[0], [ event ].concat( data ) ) === false || - event.isDefaultPrevented() ); - } -}; - -$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { - $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { - if ( typeof options === "string" ) { - options = { effect: options }; - } - var hasOptions, - effectName = !options ? - method : - options === true || typeof options === "number" ? - defaultEffect : - options.effect || defaultEffect; - options = options || {}; - if ( typeof options === "number" ) { - options = { duration: options }; - } - hasOptions = !$.isEmptyObject( options ); - options.complete = callback; - if ( options.delay ) { - element.delay( options.delay ); - } - if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { - element[ method ]( options ); - } else if ( effectName !== method && element[ effectName ] ) { - element[ effectName ]( options.duration, options.easing, callback ); - } else { - element.queue(function( next ) { - $( this )[ method ](); - if ( callback ) { - callback.call( element[ 0 ] ); - } - next(); - }); - } - }; -}); - -})( jQuery ); - -(function( $, undefined ) { - -var mouseHandled = false; -$( document ).mouseup( function() { - mouseHandled = false; -}); - -$.widget("ui.mouse", { - version: "1.10.2", - options: { - cancel: "input,textarea,button,select,option", - distance: 1, - delay: 0 - }, - _mouseInit: function() { - var that = this; - - this.element - .bind("mousedown."+this.widgetName, function(event) { - return that._mouseDown(event); - }) - .bind("click."+this.widgetName, function(event) { - if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { - $.removeData(event.target, that.widgetName + ".preventClickEvent"); - event.stopImmediatePropagation(); - return false; - } - }); - - this.started = false; - }, - - // TODO: make sure destroying one instance of mouse doesn't mess with - // other instances of mouse - _mouseDestroy: function() { - this.element.unbind("."+this.widgetName); - if ( this._mouseMoveDelegate ) { - $(document) - .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); - } - }, - - _mouseDown: function(event) { - // don't let more than one widget handle mouseStart - if( mouseHandled ) { return; } - - // we may have missed mouseup (out of window) - (this._mouseStarted && this._mouseUp(event)); - - this._mouseDownEvent = event; - - var that = this, - btnIsLeft = (event.which === 1), - // event.target.nodeName works around a bug in IE 8 with - // disabled inputs (#7620) - elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); - if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { - return true; - } - - this.mouseDelayMet = !this.options.delay; - if (!this.mouseDelayMet) { - this._mouseDelayTimer = setTimeout(function() { - that.mouseDelayMet = true; - }, this.options.delay); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = (this._mouseStart(event) !== false); - if (!this._mouseStarted) { - event.preventDefault(); - return true; - } - } - - // Click event may never have fired (Gecko & Opera) - if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { - $.removeData(event.target, this.widgetName + ".preventClickEvent"); - } - - // these delegates are required to keep context - this._mouseMoveDelegate = function(event) { - return that._mouseMove(event); - }; - this._mouseUpDelegate = function(event) { - return that._mouseUp(event); - }; - $(document) - .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .bind("mouseup."+this.widgetName, this._mouseUpDelegate); - - event.preventDefault(); - - mouseHandled = true; - return true; - }, - - _mouseMove: function(event) { - // IE mouseup check - mouseup happened when mouse was out of window - if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { - return this._mouseUp(event); - } - - if (this._mouseStarted) { - this._mouseDrag(event); - return event.preventDefault(); - } - - if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { - this._mouseStarted = - (this._mouseStart(this._mouseDownEvent, event) !== false); - (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); - } - - return !this._mouseStarted; - }, - - _mouseUp: function(event) { - $(document) - .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) - .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); - - if (this._mouseStarted) { - this._mouseStarted = false; - - if (event.target === this._mouseDownEvent.target) { - $.data(event.target, this.widgetName + ".preventClickEvent", true); - } - - this._mouseStop(event); - } - - return false; - }, - - _mouseDistanceMet: function(event) { - return (Math.max( - Math.abs(this._mouseDownEvent.pageX - event.pageX), - Math.abs(this._mouseDownEvent.pageY - event.pageY) - ) >= this.options.distance - ); - }, - - _mouseDelayMet: function(/* event */) { - return this.mouseDelayMet; - }, - - // These are placeholder methods, to be overriden by extending plugin - _mouseStart: function(/* event */) {}, - _mouseDrag: function(/* event */) {}, - _mouseStop: function(/* event */) {}, - _mouseCapture: function(/* event */) { return true; } -}); - -})(jQuery); - -(function( $, undefined ) { - -$.widget("ui.draggable", $.ui.mouse, { - version: "1.10.2", - widgetEventPrefix: "drag", - options: { - addClasses: true, - appendTo: "parent", - axis: false, - connectToSortable: false, - containment: false, - cursor: "auto", - cursorAt: false, - grid: false, - handle: false, - helper: "original", - iframeFix: false, - opacity: false, - refreshPositions: false, - revert: false, - revertDuration: 500, - scope: "default", - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - snap: false, - snapMode: "both", - snapTolerance: 20, - stack: false, - zIndex: false, - - // callbacks - drag: null, - start: null, - stop: null - }, - _create: function() { - - if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) { - this.element[0].style.position = "relative"; - } - if (this.options.addClasses){ - this.element.addClass("ui-draggable"); - } - if (this.options.disabled){ - this.element.addClass("ui-draggable-disabled"); - } - - this._mouseInit(); - - }, - - _destroy: function() { - this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); - this._mouseDestroy(); - }, - - _mouseCapture: function(event) { - - var o = this.options; - - // among others, prevent a drag on a resizable-handle - if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) { - return false; - } - - //Quit if we're not on a valid handle - this.handle = this._getHandle(event); - if (!this.handle) { - return false; - } - - $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() { - $("
") - .css({ - width: this.offsetWidth+"px", height: this.offsetHeight+"px", - position: "absolute", opacity: "0.001", zIndex: 1000 - }) - .css($(this).offset()) - .appendTo("body"); - }); - - return true; - - }, - - _mouseStart: function(event) { - - var o = this.options; - - //Create and append the visible helper - this.helper = this._createHelper(event); - - this.helper.addClass("ui-draggable-dragging"); - - //Cache the helper size - this._cacheHelperProportions(); - - //If ddmanager is used for droppables, set the global draggable - if($.ui.ddmanager) { - $.ui.ddmanager.current = this; - } - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Store the helper's css position - this.cssPosition = this.helper.css("position"); - this.scrollParent = this.helper.scrollParent(); - - //The element's absolute position on the page minus margins - this.offset = this.positionAbs = this.element.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - //Generate the original position - this.originalPosition = this.position = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if "cursorAt" is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Set a containment if given in the options - if(o.containment) { - this._setContainment(); - } - - //Trigger event + callbacks - if(this._trigger("start", event) === false) { - this._clear(); - return false; - } - - //Recache the helper size - this._cacheHelperProportions(); - - //Prepare the droppable offsets - if ($.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - - - this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position - - //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003) - if ( $.ui.ddmanager ) { - $.ui.ddmanager.dragStart(this, event); - } - - return true; - }, - - _mouseDrag: function(event, noPropagation) { - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - //Call plugins and callbacks and use the resulting position if something is returned - if (!noPropagation) { - var ui = this._uiHash(); - if(this._trigger("drag", event, ui) === false) { - this._mouseUp({}); - return false; - } - this.position = ui.position; - } - - if(!this.options.axis || this.options.axis !== "y") { - this.helper[0].style.left = this.position.left+"px"; - } - if(!this.options.axis || this.options.axis !== "x") { - this.helper[0].style.top = this.position.top+"px"; - } - if($.ui.ddmanager) { - $.ui.ddmanager.drag(this, event); - } - - return false; - }, - - _mouseStop: function(event) { - - //If we are using droppables, inform the manager about the drop - var element, - that = this, - elementInDom = false, - dropped = false; - if ($.ui.ddmanager && !this.options.dropBehaviour) { - dropped = $.ui.ddmanager.drop(this, event); - } - - //if a drop comes from outside (a sortable) - if(this.dropped) { - dropped = this.dropped; - this.dropped = false; - } - - //if the original element is no longer in the DOM don't bother to continue (see #8269) - element = this.element[0]; - while ( element && (element = element.parentNode) ) { - if (element === document ) { - elementInDom = true; - } - } - if ( !elementInDom && this.options.helper === "original" ) { - return false; - } - - if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { - $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { - if(that._trigger("stop", event) !== false) { - that._clear(); - } - }); - } else { - if(this._trigger("stop", event) !== false) { - this._clear(); - } - } - - return false; - }, - - _mouseUp: function(event) { - //Remove frame helpers - $("div.ui-draggable-iframeFix").each(function() { - this.parentNode.removeChild(this); - }); - - //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003) - if( $.ui.ddmanager ) { - $.ui.ddmanager.dragStop(this, event); - } - - return $.ui.mouse.prototype._mouseUp.call(this, event); - }, - - cancel: function() { - - if(this.helper.is(".ui-draggable-dragging")) { - this._mouseUp({}); - } else { - this._clear(); - } - - return this; - - }, - - _getHandle: function(event) { - return this.options.handle ? - !!$( event.target ).closest( this.element.find( this.options.handle ) ).length : - true; - }, - - _createHelper: function(event) { - - var o = this.options, - helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element); - - if(!helper.parents("body").length) { - helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo)); - } - - if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) { - helper.css("position", "absolute"); - } - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj === "string") { - obj = obj.split(" "); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ("left" in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ("right" in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ("top" in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ("bottom" in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - //Get the offsetParent and cache its position - this.offsetParent = this.helper.offsetParent(); - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - //This needs to be actually done for all browsers, since pageX/pageY includes this information - //Ugly IE fix - if((this.offsetParent[0] === document.body) || - (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { - po = { top: 0, left: 0 }; - } - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition === "relative") { - var p = this.element.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.element.css("marginLeft"),10) || 0), - top: (parseInt(this.element.css("marginTop"),10) || 0), - right: (parseInt(this.element.css("marginRight"),10) || 0), - bottom: (parseInt(this.element.css("marginBottom"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var over, c, ce, - o = this.options; - - if(o.containment === "parent") { - o.containment = this.helper[0].parentNode; - } - if(o.containment === "document" || o.containment === "window") { - this.containment = [ - o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left, - o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top, - (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, - (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top - ]; - } - - if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) { - c = $(o.containment); - ce = c[0]; - - if(!ce) { - return; - } - - over = ($(ce).css("overflow") !== "hidden"); - - this.containment = [ - (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0), - (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0), - (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderRightWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right, - (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderBottomWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom - ]; - this.relative_container = c; - - } else if(o.containment.constructor === Array) { - this.containment = o.containment; - } - - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) { - pos = this.position; - } - - var mod = d === "absolute" ? 1 : -1, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - return { - top: ( - pos.top + // The absolute mouse position - this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) - ), - left: ( - pos.left + // The absolute mouse position - this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) - ) - }; - - }, - - _generatePosition: function(event) { - - var containment, co, top, left, - o = this.options, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, - scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName), - pageX = event.pageX, - pageY = event.pageY; - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - if(this.originalPosition) { //If we are not dragging yet, we won't check for options - if(this.containment) { - if (this.relative_container){ - co = this.relative_container.offset(); - containment = [ this.containment[0] + co.left, - this.containment[1] + co.top, - this.containment[2] + co.left, - this.containment[3] + co.top ]; - } - else { - containment = this.containment; - } - - if(event.pageX - this.offset.click.left < containment[0]) { - pageX = containment[0] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top < containment[1]) { - pageY = containment[1] + this.offset.click.top; - } - if(event.pageX - this.offset.click.left > containment[2]) { - pageX = containment[2] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top > containment[3]) { - pageY = containment[3] + this.offset.click.top; - } - } - - if(o.grid) { - //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950) - top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY; - pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX; - pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY - // The absolute mouse position - this.offset.click.top - // Click offset (relative to the element) - this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) - ), - left: ( - pageX - // The absolute mouse position - this.offset.click.left - // Click offset (relative to the element) - this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) - ) - }; - - }, - - _clear: function() { - this.helper.removeClass("ui-draggable-dragging"); - if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) { - this.helper.remove(); - } - this.helper = null; - this.cancelHelperRemoval = false; - }, - - // From now on bulk stuff - mainly helpers - - _trigger: function(type, event, ui) { - ui = ui || this._uiHash(); - $.ui.plugin.call(this, type, [event, ui]); - //The absolute position has to be recalculated after plugins - if(type === "drag") { - this.positionAbs = this._convertPositionTo("absolute"); - } - return $.Widget.prototype._trigger.call(this, type, event, ui); - }, - - plugins: {}, - - _uiHash: function() { - return { - helper: this.helper, - position: this.position, - originalPosition: this.originalPosition, - offset: this.positionAbs - }; - } - -}); - -$.ui.plugin.add("draggable", "connectToSortable", { - start: function(event, ui) { - - var inst = $(this).data("ui-draggable"), o = inst.options, - uiSortable = $.extend({}, ui, { item: inst.element }); - inst.sortables = []; - $(o.connectToSortable).each(function() { - var sortable = $.data(this, "ui-sortable"); - if (sortable && !sortable.options.disabled) { - inst.sortables.push({ - instance: sortable, - shouldRevert: sortable.options.revert - }); - sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page). - sortable._trigger("activate", event, uiSortable); - } - }); - - }, - stop: function(event, ui) { - - //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper - var inst = $(this).data("ui-draggable"), - uiSortable = $.extend({}, ui, { item: inst.element }); - - $.each(inst.sortables, function() { - if(this.instance.isOver) { - - this.instance.isOver = 0; - - inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance - this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) - - //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid" - if(this.shouldRevert) { - this.instance.options.revert = this.shouldRevert; - } - - //Trigger the stop of the sortable - this.instance._mouseStop(event); - - this.instance.options.helper = this.instance.options._helper; - - //If the helper has been the original item, restore properties in the sortable - if(inst.options.helper === "original") { - this.instance.currentItem.css({ top: "auto", left: "auto" }); - } - - } else { - this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance - this.instance._trigger("deactivate", event, uiSortable); - } - - }); - - }, - drag: function(event, ui) { - - var inst = $(this).data("ui-draggable"), that = this; - - $.each(inst.sortables, function() { - - var innermostIntersecting = false, - thisSortable = this; - - //Copy over some variables to allow calling the sortable's native _intersectsWith - this.instance.positionAbs = inst.positionAbs; - this.instance.helperProportions = inst.helperProportions; - this.instance.offset.click = inst.offset.click; - - if(this.instance._intersectsWith(this.instance.containerCache)) { - innermostIntersecting = true; - $.each(inst.sortables, function () { - this.instance.positionAbs = inst.positionAbs; - this.instance.helperProportions = inst.helperProportions; - this.instance.offset.click = inst.offset.click; - if (this !== thisSortable && - this.instance._intersectsWith(this.instance.containerCache) && - $.contains(thisSortable.instance.element[0], this.instance.element[0]) - ) { - innermostIntersecting = false; - } - return innermostIntersecting; - }); - } - - - if(innermostIntersecting) { - //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once - if(!this.instance.isOver) { - - this.instance.isOver = 1; - //Now we fake the start of dragging for the sortable instance, - //by cloning the list group item, appending it to the sortable and using it as inst.currentItem - //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) - this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true); - this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it - this.instance.options.helper = function() { return ui.helper[0]; }; - - event.target = this.instance.currentItem[0]; - this.instance._mouseCapture(event, true); - this.instance._mouseStart(event, true, true); - - //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes - this.instance.offset.click.top = inst.offset.click.top; - this.instance.offset.click.left = inst.offset.click.left; - this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; - this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; - - inst._trigger("toSortable", event); - inst.dropped = this.instance.element; //draggable revert needs that - //hack so receive/update callbacks work (mostly) - inst.currentItem = inst.element; - this.instance.fromOutside = inst; - - } - - //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable - if(this.instance.currentItem) { - this.instance._mouseDrag(event); - } - - } else { - - //If it doesn't intersect with the sortable, and it intersected before, - //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval - if(this.instance.isOver) { - - this.instance.isOver = 0; - this.instance.cancelHelperRemoval = true; - - //Prevent reverting on this forced stop - this.instance.options.revert = false; - - // The out event needs to be triggered independently - this.instance._trigger("out", event, this.instance._uiHash(this.instance)); - - this.instance._mouseStop(event, true); - this.instance.options.helper = this.instance.options._helper; - - //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size - this.instance.currentItem.remove(); - if(this.instance.placeholder) { - this.instance.placeholder.remove(); - } - - inst._trigger("fromSortable", event); - inst.dropped = false; //draggable revert needs that - } - - } - - }); - - } -}); - -$.ui.plugin.add("draggable", "cursor", { - start: function() { - var t = $("body"), o = $(this).data("ui-draggable").options; - if (t.css("cursor")) { - o._cursor = t.css("cursor"); - } - t.css("cursor", o.cursor); - }, - stop: function() { - var o = $(this).data("ui-draggable").options; - if (o._cursor) { - $("body").css("cursor", o._cursor); - } - } -}); - -$.ui.plugin.add("draggable", "opacity", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data("ui-draggable").options; - if(t.css("opacity")) { - o._opacity = t.css("opacity"); - } - t.css("opacity", o.opacity); - }, - stop: function(event, ui) { - var o = $(this).data("ui-draggable").options; - if(o._opacity) { - $(ui.helper).css("opacity", o._opacity); - } - } -}); - -$.ui.plugin.add("draggable", "scroll", { - start: function() { - var i = $(this).data("ui-draggable"); - if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { - i.overflowOffset = i.scrollParent.offset(); - } - }, - drag: function( event ) { - - var i = $(this).data("ui-draggable"), o = i.options, scrolled = false; - - if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") { - - if(!o.axis || o.axis !== "x") { - if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; - } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) { - i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; - } - } - - if(!o.axis || o.axis !== "y") { - if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; - } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) { - i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; - } - } - - } else { - - if(!o.axis || o.axis !== "x") { - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - } - } - - if(!o.axis || o.axis !== "y") { - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - } - } - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(i, event); - } - - } -}); - -$.ui.plugin.add("draggable", "snap", { - start: function() { - - var i = $(this).data("ui-draggable"), - o = i.options; - - i.snapElements = []; - - $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() { - var $t = $(this), - $o = $t.offset(); - if(this !== i.element[0]) { - i.snapElements.push({ - item: this, - width: $t.outerWidth(), height: $t.outerHeight(), - top: $o.top, left: $o.left - }); - } - }); - - }, - drag: function(event, ui) { - - var ts, bs, ls, rs, l, r, t, b, i, first, - inst = $(this).data("ui-draggable"), - o = inst.options, - d = o.snapTolerance, - x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, - y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; - - for (i = inst.snapElements.length - 1; i >= 0; i--){ - - l = inst.snapElements[i].left; - r = l + inst.snapElements[i].width; - t = inst.snapElements[i].top; - b = t + inst.snapElements[i].height; - - //Yes, I know, this is insane ;) - if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { - if(inst.snapElements[i].snapping) { - (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - } - inst.snapElements[i].snapping = false; - continue; - } - - if(o.snapMode !== "inner") { - ts = Math.abs(t - y2) <= d; - bs = Math.abs(b - y1) <= d; - ls = Math.abs(l - x2) <= d; - rs = Math.abs(r - x1) <= d; - if(ts) { - ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - } - if(bs) { - ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top; - } - if(ls) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left; - } - if(rs) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left; - } - } - - first = (ts || bs || ls || rs); - - if(o.snapMode !== "outer") { - ts = Math.abs(t - y1) <= d; - bs = Math.abs(b - y2) <= d; - ls = Math.abs(l - x1) <= d; - rs = Math.abs(r - x2) <= d; - if(ts) { - ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top; - } - if(bs) { - ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top; - } - if(ls) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left; - } - if(rs) { - ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left; - } - } - - if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) { - (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); - } - inst.snapElements[i].snapping = (ts || bs || ls || rs || first); - - } - - } -}); - -$.ui.plugin.add("draggable", "stack", { - start: function() { - var min, - o = this.data("ui-draggable").options, - group = $.makeArray($(o.stack)).sort(function(a,b) { - return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0); - }); - - if (!group.length) { return; } - - min = parseInt($(group[0]).css("zIndex"), 10) || 0; - $(group).each(function(i) { - $(this).css("zIndex", min + i); - }); - this.css("zIndex", (min + group.length)); - } -}); - -$.ui.plugin.add("draggable", "zIndex", { - start: function(event, ui) { - var t = $(ui.helper), o = $(this).data("ui-draggable").options; - if(t.css("zIndex")) { - o._zIndex = t.css("zIndex"); - } - t.css("zIndex", o.zIndex); - }, - stop: function(event, ui) { - var o = $(this).data("ui-draggable").options; - if(o._zIndex) { - $(ui.helper).css("zIndex", o._zIndex); - } - } -}); - -})(jQuery); - -(function( $, undefined ) { - -function isOverAxis( x, reference, size ) { - return ( x > reference ) && ( x < ( reference + size ) ); -} - -$.widget("ui.droppable", { - version: "1.10.2", - widgetEventPrefix: "drop", - options: { - accept: "*", - activeClass: false, - addClasses: true, - greedy: false, - hoverClass: false, - scope: "default", - tolerance: "intersect", - - // callbacks - activate: null, - deactivate: null, - drop: null, - out: null, - over: null - }, - _create: function() { - - var o = this.options, - accept = o.accept; - - this.isover = false; - this.isout = true; - - this.accept = $.isFunction(accept) ? accept : function(d) { - return d.is(accept); - }; - - //Store the droppable's proportions - this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; - - // Add the reference and positions to the manager - $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; - $.ui.ddmanager.droppables[o.scope].push(this); - - (o.addClasses && this.element.addClass("ui-droppable")); - - }, - - _destroy: function() { - var i = 0, - drop = $.ui.ddmanager.droppables[this.options.scope]; - - for ( ; i < drop.length; i++ ) { - if ( drop[i] === this ) { - drop.splice(i, 1); - } - } - - this.element.removeClass("ui-droppable ui-droppable-disabled"); - }, - - _setOption: function(key, value) { - - if(key === "accept") { - this.accept = $.isFunction(value) ? value : function(d) { - return d.is(value); - }; - } - $.Widget.prototype._setOption.apply(this, arguments); - }, - - _activate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) { - this.element.addClass(this.options.activeClass); - } - if(draggable){ - this._trigger("activate", event, this.ui(draggable)); - } - }, - - _deactivate: function(event) { - var draggable = $.ui.ddmanager.current; - if(this.options.activeClass) { - this.element.removeClass(this.options.activeClass); - } - if(draggable){ - this._trigger("deactivate", event, this.ui(draggable)); - } - }, - - _over: function(event) { - - var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.addClass(this.options.hoverClass); - } - this._trigger("over", event, this.ui(draggable)); - } - - }, - - _out: function(event) { - - var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } - - if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.removeClass(this.options.hoverClass); - } - this._trigger("out", event, this.ui(draggable)); - } - - }, - - _drop: function(event,custom) { - - var draggable = custom || $.ui.ddmanager.current, - childrenIntersection = false; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return false; - } - - this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() { - var inst = $.data(this, "ui-droppable"); - if( - inst.options.greedy && - !inst.options.disabled && - inst.options.scope === draggable.options.scope && - inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) && - $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) - ) { childrenIntersection = true; return false; } - }); - if(childrenIntersection) { - return false; - } - - if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.activeClass) { - this.element.removeClass(this.options.activeClass); - } - if(this.options.hoverClass) { - this.element.removeClass(this.options.hoverClass); - } - this._trigger("drop", event, this.ui(draggable)); - return this.element; - } - - return false; - - }, - - ui: function(c) { - return { - draggable: (c.currentItem || c.element), - helper: c.helper, - position: c.position, - offset: c.positionAbs - }; - } - -}); - -$.ui.intersect = function(draggable, droppable, toleranceMode) { - - if (!droppable.offset) { - return false; - } - - var draggableLeft, draggableTop, - x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, - y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height, - l = droppable.offset.left, r = l + droppable.proportions.width, - t = droppable.offset.top, b = t + droppable.proportions.height; - - switch (toleranceMode) { - case "fit": - return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); - case "intersect": - return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half - x2 - (draggable.helperProportions.width / 2) < r && // Left Half - t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half - y2 - (draggable.helperProportions.height / 2) < b ); // Top Half - case "pointer": - draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left); - draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top); - return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width ); - case "touch": - return ( - (y1 >= t && y1 <= b) || // Top edge touching - (y2 >= t && y2 <= b) || // Bottom edge touching - (y1 < t && y2 > b) // Surrounded vertically - ) && ( - (x1 >= l && x1 <= r) || // Left edge touching - (x2 >= l && x2 <= r) || // Right edge touching - (x1 < l && x2 > r) // Surrounded horizontally - ); - default: - return false; - } - -}; - -/* - This manager tracks offsets of draggables and droppables -*/ -$.ui.ddmanager = { - current: null, - droppables: { "default": [] }, - prepareOffsets: function(t, event) { - - var i, j, - m = $.ui.ddmanager.droppables[t.options.scope] || [], - type = event ? event.type : null, // workaround for #2317 - list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack(); - - droppablesLoop: for (i = 0; i < m.length; i++) { - - //No disabled and non-accepted - if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) { - continue; - } - - // Filter out elements in the current dragged item - for (j=0; j < list.length; j++) { - if(list[j] === m[i].element[0]) { - m[i].proportions.height = 0; - continue droppablesLoop; - } - } - - m[i].visible = m[i].element.css("display") !== "none"; - if(!m[i].visible) { - continue; - } - - //Activate the droppable if used directly from draggables - if(type === "mousedown") { - m[i]._activate.call(m[i], event); - } - - m[i].offset = m[i].element.offset(); - m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; - - } - - }, - drop: function(draggable, event) { - - var dropped = false; - // Create a copy of the droppables in case the list changes during the drop (#9116) - $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() { - - if(!this.options) { - return; - } - if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) { - dropped = this._drop.call(this, event) || dropped; - } - - if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - this.isout = true; - this.isover = false; - this._deactivate.call(this, event); - } - - }); - return dropped; - - }, - dragStart: function( draggable, event ) { - //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003) - draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() { - if( !draggable.options.refreshPositions ) { - $.ui.ddmanager.prepareOffsets( draggable, event ); - } - }); - }, - drag: function(draggable, event) { - - //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. - if(draggable.options.refreshPositions) { - $.ui.ddmanager.prepareOffsets(draggable, event); - } - - //Run through all droppables and check their positions based on specific tolerance options - $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { - - if(this.options.disabled || this.greedyChild || !this.visible) { - return; - } - - var parentInstance, scope, parent, - intersects = $.ui.intersect(draggable, this, this.options.tolerance), - c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null); - if(!c) { - return; - } - - if (this.options.greedy) { - // find droppable parents with same scope - scope = this.options.scope; - parent = this.element.parents(":data(ui-droppable)").filter(function () { - return $.data(this, "ui-droppable").options.scope === scope; - }); - - if (parent.length) { - parentInstance = $.data(parent[0], "ui-droppable"); - parentInstance.greedyChild = (c === "isover"); - } - } - - // we just moved into a greedy child - if (parentInstance && c === "isover") { - parentInstance.isover = false; - parentInstance.isout = true; - parentInstance._out.call(parentInstance, event); - } - - this[c] = true; - this[c === "isout" ? "isover" : "isout"] = false; - this[c === "isover" ? "_over" : "_out"].call(this, event); - - // we just moved out of a greedy child - if (parentInstance && c === "isout") { - parentInstance.isout = false; - parentInstance.isover = true; - parentInstance._over.call(parentInstance, event); - } - }); - - }, - dragStop: function( draggable, event ) { - draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" ); - //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003) - if( !draggable.options.refreshPositions ) { - $.ui.ddmanager.prepareOffsets( draggable, event ); - } - } -}; - -})(jQuery); - -(function( $, undefined ) { - -function num(v) { - return parseInt(v, 10) || 0; -} - -function isNumber(value) { - return !isNaN(parseInt(value, 10)); -} - -$.widget("ui.resizable", $.ui.mouse, { - version: "1.10.2", - widgetEventPrefix: "resize", - options: { - alsoResize: false, - animate: false, - animateDuration: "slow", - animateEasing: "swing", - aspectRatio: false, - autoHide: false, - containment: false, - ghost: false, - grid: false, - handles: "e,s,se", - helper: false, - maxHeight: null, - maxWidth: null, - minHeight: 10, - minWidth: 10, - // See #7960 - zIndex: 90, - - // callbacks - resize: null, - start: null, - stop: null - }, - _create: function() { - - var n, i, handle, axis, hname, - that = this, - o = this.options; - this.element.addClass("ui-resizable"); - - $.extend(this, { - _aspectRatio: !!(o.aspectRatio), - aspectRatio: o.aspectRatio, - originalElement: this.element, - _proportionallyResizeElements: [], - _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null - }); - - //Wrap the element if it cannot hold child nodes - if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) { - - //Create a wrapper element and set the wrapper to the new current internal element - this.element.wrap( - $("
").css({ - position: this.element.css("position"), - width: this.element.outerWidth(), - height: this.element.outerHeight(), - top: this.element.css("top"), - left: this.element.css("left") - }) - ); - - //Overwrite the original this.element - this.element = this.element.parent().data( - "ui-resizable", this.element.data("ui-resizable") - ); - - this.elementIsWrapper = true; - - //Move margins to the wrapper - this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") }); - this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0}); - - //Prevent Safari textarea resize - this.originalResizeStyle = this.originalElement.css("resize"); - this.originalElement.css("resize", "none"); - - //Push the actual element to our proportionallyResize internal array - this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" })); - - // avoid IE jump (hard set the margin) - this.originalElement.css({ margin: this.originalElement.css("margin") }); - - // fix handlers offset - this._proportionallyResize(); - - } - - this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" }); - if(this.handles.constructor === String) { - - if ( this.handles === "all") { - this.handles = "n,e,s,w,se,sw,ne,nw"; - } - - n = this.handles.split(","); - this.handles = {}; - - for(i = 0; i < n.length; i++) { - - handle = $.trim(n[i]); - hname = "ui-resizable-"+handle; - axis = $("
"); - - // Apply zIndex to all handles - see #7960 - axis.css({ zIndex: o.zIndex }); - - //TODO : What's going on here? - if ("se" === handle) { - axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se"); - } - - //Insert into internal handles object and append to element - this.handles[handle] = ".ui-resizable-"+handle; - this.element.append(axis); - } - - } - - this._renderAxis = function(target) { - - var i, axis, padPos, padWrapper; - - target = target || this.element; - - for(i in this.handles) { - - if(this.handles[i].constructor === String) { - this.handles[i] = $(this.handles[i], this.element).show(); - } - - //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls) - if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) { - - axis = $(this.handles[i], this.element); - - //Checking the correct pad and border - padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth(); - - //The padding type i have to apply... - padPos = [ "padding", - /ne|nw|n/.test(i) ? "Top" : - /se|sw|s/.test(i) ? "Bottom" : - /^e$/.test(i) ? "Right" : "Left" ].join(""); - - target.css(padPos, padWrapper); - - this._proportionallyResize(); - - } - - //TODO: What's that good for? There's not anything to be executed left - if(!$(this.handles[i]).length) { - continue; - } - } - }; - - //TODO: make renderAxis a prototype function - this._renderAxis(this.element); - - this._handles = $(".ui-resizable-handle", this.element) - .disableSelection(); - - //Matching axis name - this._handles.mouseover(function() { - if (!that.resizing) { - if (this.className) { - axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i); - } - //Axis, default = se - that.axis = axis && axis[1] ? axis[1] : "se"; - } - }); - - //If we want to auto hide the elements - if (o.autoHide) { - this._handles.hide(); - $(this.element) - .addClass("ui-resizable-autohide") - .mouseenter(function() { - if (o.disabled) { - return; - } - $(this).removeClass("ui-resizable-autohide"); - that._handles.show(); - }) - .mouseleave(function(){ - if (o.disabled) { - return; - } - if (!that.resizing) { - $(this).addClass("ui-resizable-autohide"); - that._handles.hide(); - } - }); - } - - //Initialize the mouse interaction - this._mouseInit(); - - }, - - _destroy: function() { - - this._mouseDestroy(); - - var wrapper, - _destroy = function(exp) { - $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing") - .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove(); - }; - - //TODO: Unwrap at same DOM position - if (this.elementIsWrapper) { - _destroy(this.element); - wrapper = this.element; - this.originalElement.css({ - position: wrapper.css("position"), - width: wrapper.outerWidth(), - height: wrapper.outerHeight(), - top: wrapper.css("top"), - left: wrapper.css("left") - }).insertAfter( wrapper ); - wrapper.remove(); - } - - this.originalElement.css("resize", this.originalResizeStyle); - _destroy(this.originalElement); - - return this; - }, - - _mouseCapture: function(event) { - var i, handle, - capture = false; - - for (i in this.handles) { - handle = $(this.handles[i])[0]; - if (handle === event.target || $.contains(handle, event.target)) { - capture = true; - } - } - - return !this.options.disabled && capture; - }, - - _mouseStart: function(event) { - - var curleft, curtop, cursor, - o = this.options, - iniPos = this.element.position(), - el = this.element; - - this.resizing = true; - - // bugfix for http://dev.jquery.com/ticket/1749 - if ( (/absolute/).test( el.css("position") ) ) { - el.css({ position: "absolute", top: el.css("top"), left: el.css("left") }); - } else if (el.is(".ui-draggable")) { - el.css({ position: "absolute", top: iniPos.top, left: iniPos.left }); - } - - this._renderProxy(); - - curleft = num(this.helper.css("left")); - curtop = num(this.helper.css("top")); - - if (o.containment) { - curleft += $(o.containment).scrollLeft() || 0; - curtop += $(o.containment).scrollTop() || 0; - } - - //Store needed variables - this.offset = this.helper.offset(); - this.position = { left: curleft, top: curtop }; - this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() }; - this.originalPosition = { left: curleft, top: curtop }; - this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() }; - this.originalMousePosition = { left: event.pageX, top: event.pageY }; - - //Aspect Ratio - this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1); - - cursor = $(".ui-resizable-" + this.axis).css("cursor"); - $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor); - - el.addClass("ui-resizable-resizing"); - this._propagate("start", event); - return true; - }, - - _mouseDrag: function(event) { - - //Increase performance, avoid regex - var data, - el = this.helper, props = {}, - smp = this.originalMousePosition, - a = this.axis, - prevTop = this.position.top, - prevLeft = this.position.left, - prevWidth = this.size.width, - prevHeight = this.size.height, - dx = (event.pageX-smp.left)||0, - dy = (event.pageY-smp.top)||0, - trigger = this._change[a]; - - if (!trigger) { - return false; - } - - // Calculate the attrs that will be change - data = trigger.apply(this, [event, dx, dy]); - - // Put this in the mouseDrag handler since the user can start pressing shift while resizing - this._updateVirtualBoundaries(event.shiftKey); - if (this._aspectRatio || event.shiftKey) { - data = this._updateRatio(data, event); - } - - data = this._respectSize(data, event); - - this._updateCache(data); - - // plugins callbacks need to be called first - this._propagate("resize", event); - - if (this.position.top !== prevTop) { - props.top = this.position.top + "px"; - } - if (this.position.left !== prevLeft) { - props.left = this.position.left + "px"; - } - if (this.size.width !== prevWidth) { - props.width = this.size.width + "px"; - } - if (this.size.height !== prevHeight) { - props.height = this.size.height + "px"; - } - el.css(props); - - if (!this._helper && this._proportionallyResizeElements.length) { - this._proportionallyResize(); - } - - // Call the user callback if the element was resized - if ( ! $.isEmptyObject(props) ) { - this._trigger("resize", event, this.ui()); - } - - return false; - }, - - _mouseStop: function(event) { - - this.resizing = false; - var pr, ista, soffseth, soffsetw, s, left, top, - o = this.options, that = this; - - if(this._helper) { - - pr = this._proportionallyResizeElements; - ista = pr.length && (/textarea/i).test(pr[0].nodeName); - soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height; - soffsetw = ista ? 0 : that.sizeDiff.width; - - s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) }; - left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null; - top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; - - if (!o.animate) { - this.element.css($.extend(s, { top: top, left: left })); - } - - that.helper.height(that.size.height); - that.helper.width(that.size.width); - - if (this._helper && !o.animate) { - this._proportionallyResize(); - } - } - - $("body").css("cursor", "auto"); - - this.element.removeClass("ui-resizable-resizing"); - - this._propagate("stop", event); - - if (this._helper) { - this.helper.remove(); - } - - return false; - - }, - - _updateVirtualBoundaries: function(forceAspectRatio) { - var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b, - o = this.options; - - b = { - minWidth: isNumber(o.minWidth) ? o.minWidth : 0, - maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity, - minHeight: isNumber(o.minHeight) ? o.minHeight : 0, - maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity - }; - - if(this._aspectRatio || forceAspectRatio) { - // We want to create an enclosing box whose aspect ration is the requested one - // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension - pMinWidth = b.minHeight * this.aspectRatio; - pMinHeight = b.minWidth / this.aspectRatio; - pMaxWidth = b.maxHeight * this.aspectRatio; - pMaxHeight = b.maxWidth / this.aspectRatio; - - if(pMinWidth > b.minWidth) { - b.minWidth = pMinWidth; - } - if(pMinHeight > b.minHeight) { - b.minHeight = pMinHeight; - } - if(pMaxWidth < b.maxWidth) { - b.maxWidth = pMaxWidth; - } - if(pMaxHeight < b.maxHeight) { - b.maxHeight = pMaxHeight; - } - } - this._vBoundaries = b; - }, - - _updateCache: function(data) { - this.offset = this.helper.offset(); - if (isNumber(data.left)) { - this.position.left = data.left; - } - if (isNumber(data.top)) { - this.position.top = data.top; - } - if (isNumber(data.height)) { - this.size.height = data.height; - } - if (isNumber(data.width)) { - this.size.width = data.width; - } - }, - - _updateRatio: function( data ) { - - var cpos = this.position, - csize = this.size, - a = this.axis; - - if (isNumber(data.height)) { - data.width = (data.height * this.aspectRatio); - } else if (isNumber(data.width)) { - data.height = (data.width / this.aspectRatio); - } - - if (a === "sw") { - data.left = cpos.left + (csize.width - data.width); - data.top = null; - } - if (a === "nw") { - data.top = cpos.top + (csize.height - data.height); - data.left = cpos.left + (csize.width - data.width); - } - - return data; - }, - - _respectSize: function( data ) { - - var o = this._vBoundaries, - a = this.axis, - ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height), - isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height), - dw = this.originalPosition.left + this.originalSize.width, - dh = this.position.top + this.size.height, - cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a); - if (isminw) { - data.width = o.minWidth; - } - if (isminh) { - data.height = o.minHeight; - } - if (ismaxw) { - data.width = o.maxWidth; - } - if (ismaxh) { - data.height = o.maxHeight; - } - - if (isminw && cw) { - data.left = dw - o.minWidth; - } - if (ismaxw && cw) { - data.left = dw - o.maxWidth; - } - if (isminh && ch) { - data.top = dh - o.minHeight; - } - if (ismaxh && ch) { - data.top = dh - o.maxHeight; - } - - // fixing jump error on top/left - bug #2330 - if (!data.width && !data.height && !data.left && data.top) { - data.top = null; - } else if (!data.width && !data.height && !data.top && data.left) { - data.left = null; - } - - return data; - }, - - _proportionallyResize: function() { - - if (!this._proportionallyResizeElements.length) { - return; - } - - var i, j, borders, paddings, prel, - element = this.helper || this.element; - - for ( i=0; i < this._proportionallyResizeElements.length; i++) { - - prel = this._proportionallyResizeElements[i]; - - if (!this.borderDif) { - this.borderDif = []; - borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")]; - paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")]; - - for ( j = 0; j < borders.length; j++ ) { - this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 ); - } - } - - prel.css({ - height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0, - width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0 - }); - - } - - }, - - _renderProxy: function() { - - var el = this.element, o = this.options; - this.elementOffset = el.offset(); - - if(this._helper) { - - this.helper = this.helper || $("
"); - - this.helper.addClass(this._helper).css({ - width: this.element.outerWidth() - 1, - height: this.element.outerHeight() - 1, - position: "absolute", - left: this.elementOffset.left +"px", - top: this.elementOffset.top +"px", - zIndex: ++o.zIndex //TODO: Don't modify option - }); - - this.helper - .appendTo("body") - .disableSelection(); - - } else { - this.helper = this.element; - } - - }, - - _change: { - e: function(event, dx) { - return { width: this.originalSize.width + dx }; - }, - w: function(event, dx) { - var cs = this.originalSize, sp = this.originalPosition; - return { left: sp.left + dx, width: cs.width - dx }; - }, - n: function(event, dx, dy) { - var cs = this.originalSize, sp = this.originalPosition; - return { top: sp.top + dy, height: cs.height - dy }; - }, - s: function(event, dx, dy) { - return { height: this.originalSize.height + dy }; - }, - se: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - sw: function(event, dx, dy) { - return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - }, - ne: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy])); - }, - nw: function(event, dx, dy) { - return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy])); - } - }, - - _propagate: function(n, event) { - $.ui.plugin.call(this, n, [event, this.ui()]); - (n !== "resize" && this._trigger(n, event, this.ui())); - }, - - plugins: {}, - - ui: function() { - return { - originalElement: this.originalElement, - element: this.element, - helper: this.helper, - position: this.position, - size: this.size, - originalSize: this.originalSize, - originalPosition: this.originalPosition - }; - } - -}); - -/* - * Resizable Extensions - */ - -$.ui.plugin.add("resizable", "animate", { - - stop: function( event ) { - var that = $(this).data("ui-resizable"), - o = that.options, - pr = that._proportionallyResizeElements, - ista = pr.length && (/textarea/i).test(pr[0].nodeName), - soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height, - soffsetw = ista ? 0 : that.sizeDiff.width, - style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) }, - left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null, - top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null; - - that.element.animate( - $.extend(style, top && left ? { top: top, left: left } : {}), { - duration: o.animateDuration, - easing: o.animateEasing, - step: function() { - - var data = { - width: parseInt(that.element.css("width"), 10), - height: parseInt(that.element.css("height"), 10), - top: parseInt(that.element.css("top"), 10), - left: parseInt(that.element.css("left"), 10) - }; - - if (pr && pr.length) { - $(pr[0]).css({ width: data.width, height: data.height }); - } - - // propagating resize, and updating values for each animation step - that._updateCache(data); - that._propagate("resize", event); - - } - } - ); - } - -}); - -$.ui.plugin.add("resizable", "containment", { - - start: function() { - var element, p, co, ch, cw, width, height, - that = $(this).data("ui-resizable"), - o = that.options, - el = that.element, - oc = o.containment, - ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc; - - if (!ce) { - return; - } - - that.containerElement = $(ce); - - if (/document/.test(oc) || oc === document) { - that.containerOffset = { left: 0, top: 0 }; - that.containerPosition = { left: 0, top: 0 }; - - that.parentData = { - element: $(document), left: 0, top: 0, - width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight - }; - } - - // i'm a node, so compute top, left, right, bottom - else { - element = $(ce); - p = []; - $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); }); - - that.containerOffset = element.offset(); - that.containerPosition = element.position(); - that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) }; - - co = that.containerOffset; - ch = that.containerSize.height; - cw = that.containerSize.width; - width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ); - height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch); - - that.parentData = { - element: ce, left: co.left, top: co.top, width: width, height: height - }; - } - }, - - resize: function( event ) { - var woset, hoset, isParent, isOffsetRelative, - that = $(this).data("ui-resizable"), - o = that.options, - co = that.containerOffset, cp = that.position, - pRatio = that._aspectRatio || event.shiftKey, - cop = { top:0, left:0 }, ce = that.containerElement; - - if (ce[0] !== document && (/static/).test(ce.css("position"))) { - cop = co; - } - - if (cp.left < (that._helper ? co.left : 0)) { - that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left)); - if (pRatio) { - that.size.height = that.size.width / that.aspectRatio; - } - that.position.left = o.helper ? co.left : 0; - } - - if (cp.top < (that._helper ? co.top : 0)) { - that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top); - if (pRatio) { - that.size.width = that.size.height * that.aspectRatio; - } - that.position.top = that._helper ? co.top : 0; - } - - that.offset.left = that.parentData.left+that.position.left; - that.offset.top = that.parentData.top+that.position.top; - - woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width ); - hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height ); - - isParent = that.containerElement.get(0) === that.element.parent().get(0); - isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position")); - - if(isParent && isOffsetRelative) { - woset -= that.parentData.left; - } - - if (woset + that.size.width >= that.parentData.width) { - that.size.width = that.parentData.width - woset; - if (pRatio) { - that.size.height = that.size.width / that.aspectRatio; - } - } - - if (hoset + that.size.height >= that.parentData.height) { - that.size.height = that.parentData.height - hoset; - if (pRatio) { - that.size.width = that.size.height * that.aspectRatio; - } - } - }, - - stop: function(){ - var that = $(this).data("ui-resizable"), - o = that.options, - co = that.containerOffset, - cop = that.containerPosition, - ce = that.containerElement, - helper = $(that.helper), - ho = helper.offset(), - w = helper.outerWidth() - that.sizeDiff.width, - h = helper.outerHeight() - that.sizeDiff.height; - - if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) { - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - } - - if (that._helper && !o.animate && (/static/).test(ce.css("position"))) { - $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h }); - } - - } -}); - -$.ui.plugin.add("resizable", "alsoResize", { - - start: function () { - var that = $(this).data("ui-resizable"), - o = that.options, - _store = function (exp) { - $(exp).each(function() { - var el = $(this); - el.data("ui-resizable-alsoresize", { - width: parseInt(el.width(), 10), height: parseInt(el.height(), 10), - left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10) - }); - }); - }; - - if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) { - if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); } - else { $.each(o.alsoResize, function (exp) { _store(exp); }); } - }else{ - _store(o.alsoResize); - } - }, - - resize: function (event, ui) { - var that = $(this).data("ui-resizable"), - o = that.options, - os = that.originalSize, - op = that.originalPosition, - delta = { - height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0, - top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0 - }, - - _alsoResize = function (exp, c) { - $(exp).each(function() { - var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {}, - css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"]; - - $.each(css, function (i, prop) { - var sum = (start[prop]||0) + (delta[prop]||0); - if (sum && sum >= 0) { - style[prop] = sum || null; - } - }); - - el.css(style); - }); - }; - - if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) { - $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); }); - }else{ - _alsoResize(o.alsoResize); - } - }, - - stop: function () { - $(this).removeData("resizable-alsoresize"); - } -}); - -$.ui.plugin.add("resizable", "ghost", { - - start: function() { - - var that = $(this).data("ui-resizable"), o = that.options, cs = that.size; - - that.ghost = that.originalElement.clone(); - that.ghost - .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 }) - .addClass("ui-resizable-ghost") - .addClass(typeof o.ghost === "string" ? o.ghost : ""); - - that.ghost.appendTo(that.helper); - - }, - - resize: function(){ - var that = $(this).data("ui-resizable"); - if (that.ghost) { - that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width }); - } - }, - - stop: function() { - var that = $(this).data("ui-resizable"); - if (that.ghost && that.helper) { - that.helper.get(0).removeChild(that.ghost.get(0)); - } - } - -}); - -$.ui.plugin.add("resizable", "grid", { - - resize: function() { - var that = $(this).data("ui-resizable"), - o = that.options, - cs = that.size, - os = that.originalSize, - op = that.originalPosition, - a = that.axis, - grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid, - gridX = (grid[0]||1), - gridY = (grid[1]||1), - ox = Math.round((cs.width - os.width) / gridX) * gridX, - oy = Math.round((cs.height - os.height) / gridY) * gridY, - newWidth = os.width + ox, - newHeight = os.height + oy, - isMaxWidth = o.maxWidth && (o.maxWidth < newWidth), - isMaxHeight = o.maxHeight && (o.maxHeight < newHeight), - isMinWidth = o.minWidth && (o.minWidth > newWidth), - isMinHeight = o.minHeight && (o.minHeight > newHeight); - - o.grid = grid; - - if (isMinWidth) { - newWidth = newWidth + gridX; - } - if (isMinHeight) { - newHeight = newHeight + gridY; - } - if (isMaxWidth) { - newWidth = newWidth - gridX; - } - if (isMaxHeight) { - newHeight = newHeight - gridY; - } - - if (/^(se|s|e)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - } else if (/^(ne)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.top = op.top - oy; - } else if (/^(sw)$/.test(a)) { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.left = op.left - ox; - } else { - that.size.width = newWidth; - that.size.height = newHeight; - that.position.top = op.top - oy; - that.position.left = op.left - ox; - } - } - -}); - -})(jQuery); - -(function( $, undefined ) { - -$.widget("ui.selectable", $.ui.mouse, { - version: "1.10.2", - options: { - appendTo: "body", - autoRefresh: true, - distance: 0, - filter: "*", - tolerance: "touch", - - // callbacks - selected: null, - selecting: null, - start: null, - stop: null, - unselected: null, - unselecting: null - }, - _create: function() { - var selectees, - that = this; - - this.element.addClass("ui-selectable"); - - this.dragged = false; - - // cache selectee children based on filter - this.refresh = function() { - selectees = $(that.options.filter, that.element[0]); - selectees.addClass("ui-selectee"); - selectees.each(function() { - var $this = $(this), - pos = $this.offset(); - $.data(this, "selectable-item", { - element: this, - $element: $this, - left: pos.left, - top: pos.top, - right: pos.left + $this.outerWidth(), - bottom: pos.top + $this.outerHeight(), - startselected: false, - selected: $this.hasClass("ui-selected"), - selecting: $this.hasClass("ui-selecting"), - unselecting: $this.hasClass("ui-unselecting") - }); - }); - }; - this.refresh(); - - this.selectees = selectees.addClass("ui-selectee"); - - this._mouseInit(); - - this.helper = $("
"); - }, - - _destroy: function() { - this.selectees - .removeClass("ui-selectee") - .removeData("selectable-item"); - this.element - .removeClass("ui-selectable ui-selectable-disabled"); - this._mouseDestroy(); - }, - - _mouseStart: function(event) { - var that = this, - options = this.options; - - this.opos = [event.pageX, event.pageY]; - - if (this.options.disabled) { - return; - } - - this.selectees = $(options.filter, this.element[0]); - - this._trigger("start", event); - - $(options.appendTo).append(this.helper); - // position helper (lasso) - this.helper.css({ - "left": event.pageX, - "top": event.pageY, - "width": 0, - "height": 0 - }); - - if (options.autoRefresh) { - this.refresh(); - } - - this.selectees.filter(".ui-selected").each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.startselected = true; - if (!event.metaKey && !event.ctrlKey) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - }); - - $(event.target).parents().addBack().each(function() { - var doSelect, - selectee = $.data(this, "selectable-item"); - if (selectee) { - doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected"); - selectee.$element - .removeClass(doSelect ? "ui-unselecting" : "ui-selected") - .addClass(doSelect ? "ui-selecting" : "ui-unselecting"); - selectee.unselecting = !doSelect; - selectee.selecting = doSelect; - selectee.selected = doSelect; - // selectable (UN)SELECTING callback - if (doSelect) { - that._trigger("selecting", event, { - selecting: selectee.element - }); - } else { - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - return false; - } - }); - - }, - - _mouseDrag: function(event) { - - this.dragged = true; - - if (this.options.disabled) { - return; - } - - var tmp, - that = this, - options = this.options, - x1 = this.opos[0], - y1 = this.opos[1], - x2 = event.pageX, - y2 = event.pageY; - - if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; } - if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; } - this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1}); - - this.selectees.each(function() { - var selectee = $.data(this, "selectable-item"), - hit = false; - - //prevent helper from being selected if appendTo: selectable - if (!selectee || selectee.element === that.element[0]) { - return; - } - - if (options.tolerance === "touch") { - hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) ); - } else if (options.tolerance === "fit") { - hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2); - } - - if (hit) { - // SELECT - if (selectee.selected) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - } - if (selectee.unselecting) { - selectee.$element.removeClass("ui-unselecting"); - selectee.unselecting = false; - } - if (!selectee.selecting) { - selectee.$element.addClass("ui-selecting"); - selectee.selecting = true; - // selectable SELECTING callback - that._trigger("selecting", event, { - selecting: selectee.element - }); - } - } else { - // UNSELECT - if (selectee.selecting) { - if ((event.metaKey || event.ctrlKey) && selectee.startselected) { - selectee.$element.removeClass("ui-selecting"); - selectee.selecting = false; - selectee.$element.addClass("ui-selected"); - selectee.selected = true; - } else { - selectee.$element.removeClass("ui-selecting"); - selectee.selecting = false; - if (selectee.startselected) { - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - } - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - if (selectee.selected) { - if (!event.metaKey && !event.ctrlKey && !selectee.startselected) { - selectee.$element.removeClass("ui-selected"); - selectee.selected = false; - - selectee.$element.addClass("ui-unselecting"); - selectee.unselecting = true; - // selectable UNSELECTING callback - that._trigger("unselecting", event, { - unselecting: selectee.element - }); - } - } - } - }); - - return false; - }, - - _mouseStop: function(event) { - var that = this; - - this.dragged = false; - - $(".ui-unselecting", this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-unselecting"); - selectee.unselecting = false; - selectee.startselected = false; - that._trigger("unselected", event, { - unselected: selectee.element - }); - }); - $(".ui-selecting", this.element[0]).each(function() { - var selectee = $.data(this, "selectable-item"); - selectee.$element.removeClass("ui-selecting").addClass("ui-selected"); - selectee.selecting = false; - selectee.selected = true; - selectee.startselected = true; - that._trigger("selected", event, { - selected: selectee.element - }); - }); - this._trigger("stop", event); - - this.helper.remove(); - - return false; - } - -}); - -})(jQuery); - -(function( $, undefined ) { - -/*jshint loopfunc: true */ - -function isOverAxis( x, reference, size ) { - return ( x > reference ) && ( x < ( reference + size ) ); -} - -function isFloating(item) { - return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display")); -} - -$.widget("ui.sortable", $.ui.mouse, { - version: "1.10.2", - widgetEventPrefix: "sort", - ready: false, - options: { - appendTo: "parent", - axis: false, - connectWith: false, - containment: false, - cursor: "auto", - cursorAt: false, - dropOnEmpty: true, - forcePlaceholderSize: false, - forceHelperSize: false, - grid: false, - handle: false, - helper: "original", - items: "> *", - opacity: false, - placeholder: false, - revert: false, - scroll: true, - scrollSensitivity: 20, - scrollSpeed: 20, - scope: "default", - tolerance: "intersect", - zIndex: 1000, - - // callbacks - activate: null, - beforeStop: null, - change: null, - deactivate: null, - out: null, - over: null, - receive: null, - remove: null, - sort: null, - start: null, - stop: null, - update: null - }, - _create: function() { - - var o = this.options; - this.containerCache = {}; - this.element.addClass("ui-sortable"); - - //Get the items - this.refresh(); - - //Let's determine if the items are being displayed horizontally - this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false; - - //Let's determine the parent's offset - this.offset = this.element.offset(); - - //Initialize mouse events for interaction - this._mouseInit(); - - //We're ready to go - this.ready = true; - - }, - - _destroy: function() { - this.element - .removeClass("ui-sortable ui-sortable-disabled"); - this._mouseDestroy(); - - for ( var i = this.items.length - 1; i >= 0; i-- ) { - this.items[i].item.removeData(this.widgetName + "-item"); - } - - return this; - }, - - _setOption: function(key, value){ - if ( key === "disabled" ) { - this.options[ key ] = value; - - this.widget().toggleClass( "ui-sortable-disabled", !!value ); - } else { - // Don't call widget base _setOption for disable as it adds ui-state-disabled class - $.Widget.prototype._setOption.apply(this, arguments); - } - }, - - _mouseCapture: function(event, overrideHandle) { - var currentItem = null, - validHandle = false, - that = this; - - if (this.reverting) { - return false; - } - - if(this.options.disabled || this.options.type === "static") { - return false; - } - - //We have to refresh the items data once first - this._refreshItems(event); - - //Find out if the clicked node (or one of its parents) is a actual item in this.items - $(event.target).parents().each(function() { - if($.data(this, that.widgetName + "-item") === that) { - currentItem = $(this); - return false; - } - }); - if($.data(event.target, that.widgetName + "-item") === that) { - currentItem = $(event.target); - } - - if(!currentItem) { - return false; - } - if(this.options.handle && !overrideHandle) { - $(this.options.handle, currentItem).find("*").addBack().each(function() { - if(this === event.target) { - validHandle = true; - } - }); - if(!validHandle) { - return false; - } - } - - this.currentItem = currentItem; - this._removeCurrentsFromItems(); - return true; - - }, - - _mouseStart: function(event, overrideHandle, noActivation) { - - var i, body, - o = this.options; - - this.currentContainer = this; - - //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture - this.refreshPositions(); - - //Create and append the visible helper - this.helper = this._createHelper(event); - - //Cache the helper size - this._cacheHelperProportions(); - - /* - * - Position generation - - * This block generates everything position related - it's the core of draggables. - */ - - //Cache the margins of the original element - this._cacheMargins(); - - //Get the next scrolling parent - this.scrollParent = this.helper.scrollParent(); - - //The element's absolute position on the page minus margins - this.offset = this.currentItem.offset(); - this.offset = { - top: this.offset.top - this.margins.top, - left: this.offset.left - this.margins.left - }; - - $.extend(this.offset, { - click: { //Where the click happened, relative to the element - left: event.pageX - this.offset.left, - top: event.pageY - this.offset.top - }, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper - }); - - // Only after we got the offset, we can change the helper's position to absolute - // TODO: Still need to figure out a way to make relative sorting possible - this.helper.css("position", "absolute"); - this.cssPosition = this.helper.css("position"); - - //Generate the original position - this.originalPosition = this._generatePosition(event); - this.originalPageX = event.pageX; - this.originalPageY = event.pageY; - - //Adjust the mouse offset relative to the helper if "cursorAt" is supplied - (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt)); - - //Cache the former DOM position - this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] }; - - //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way - if(this.helper[0] !== this.currentItem[0]) { - this.currentItem.hide(); - } - - //Create the placeholder - this._createPlaceholder(); - - //Set a containment if given in the options - if(o.containment) { - this._setContainment(); - } - - if( o.cursor && o.cursor !== "auto" ) { // cursor option - body = this.document.find( "body" ); - - // support: IE - this.storedCursor = body.css( "cursor" ); - body.css( "cursor", o.cursor ); - - this.storedStylesheet = $( "" ).appendTo( body ); - } - - if(o.opacity) { // opacity option - if (this.helper.css("opacity")) { - this._storedOpacity = this.helper.css("opacity"); - } - this.helper.css("opacity", o.opacity); - } - - if(o.zIndex) { // zIndex option - if (this.helper.css("zIndex")) { - this._storedZIndex = this.helper.css("zIndex"); - } - this.helper.css("zIndex", o.zIndex); - } - - //Prepare scrolling - if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { - this.overflowOffset = this.scrollParent.offset(); - } - - //Call callbacks - this._trigger("start", event, this._uiHash()); - - //Recache the helper size - if(!this._preserveHelperProportions) { - this._cacheHelperProportions(); - } - - - //Post "activate" events to possible containers - if( !noActivation ) { - for ( i = this.containers.length - 1; i >= 0; i-- ) { - this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); - } - } - - //Prepare possible droppables - if($.ui.ddmanager) { - $.ui.ddmanager.current = this; - } - - if ($.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - - this.dragging = true; - - this.helper.addClass("ui-sortable-helper"); - this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position - return true; - - }, - - _mouseDrag: function(event) { - var i, item, itemElement, intersection, - o = this.options, - scrolled = false; - - //Compute the helpers position - this.position = this._generatePosition(event); - this.positionAbs = this._convertPositionTo("absolute"); - - if (!this.lastPositionAbs) { - this.lastPositionAbs = this.positionAbs; - } - - //Do scrolling - if(this.options.scroll) { - if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { - - if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; - } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { - this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; - } - - if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; - } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { - this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; - } - - } else { - - if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); - } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { - scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); - } - - if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); - } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { - scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); - } - - } - - if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { - $.ui.ddmanager.prepareOffsets(this, event); - } - } - - //Regenerate the absolute position used for position checks - this.positionAbs = this._convertPositionTo("absolute"); - - //Set the helper position - if(!this.options.axis || this.options.axis !== "y") { - this.helper[0].style.left = this.position.left+"px"; - } - if(!this.options.axis || this.options.axis !== "x") { - this.helper[0].style.top = this.position.top+"px"; - } - - //Rearrange - for (i = this.items.length - 1; i >= 0; i--) { - - //Cache variables and intersection, continue if no intersection - item = this.items[i]; - itemElement = item.item[0]; - intersection = this._intersectsWithPointer(item); - if (!intersection) { - continue; - } - - // Only put the placeholder inside the current Container, skip all - // items form other containers. This works because when moving - // an item from one container to another the - // currentContainer is switched before the placeholder is moved. - // - // Without this moving items in "sub-sortables" can cause the placeholder to jitter - // beetween the outer and inner container. - if (item.instance !== this.currentContainer) { - continue; - } - - // cannot intersect with itself - // no useless actions that have been done before - // no action if the item moved is the parent of the item checked - if (itemElement !== this.currentItem[0] && - this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && - !$.contains(this.placeholder[0], itemElement) && - (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) - ) { - - this.direction = intersection === 1 ? "down" : "up"; - - if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { - this._rearrange(event, item); - } else { - break; - } - - this._trigger("change", event, this._uiHash()); - break; - } - } - - //Post events to containers - this._contactContainers(event); - - //Interconnect with droppables - if($.ui.ddmanager) { - $.ui.ddmanager.drag(this, event); - } - - //Call callbacks - this._trigger("sort", event, this._uiHash()); - - this.lastPositionAbs = this.positionAbs; - return false; - - }, - - _mouseStop: function(event, noPropagation) { - - if(!event) { - return; - } - - //If we are using droppables, inform the manager about the drop - if ($.ui.ddmanager && !this.options.dropBehaviour) { - $.ui.ddmanager.drop(this, event); - } - - if(this.options.revert) { - var that = this, - cur = this.placeholder.offset(), - axis = this.options.axis, - animation = {}; - - if ( !axis || axis === "x" ) { - animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft); - } - if ( !axis || axis === "y" ) { - animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop); - } - this.reverting = true; - $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() { - that._clear(event); - }); - } else { - this._clear(event, noPropagation); - } - - return false; - - }, - - cancel: function() { - - if(this.dragging) { - - this._mouseUp({ target: null }); - - if(this.options.helper === "original") { - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - } else { - this.currentItem.show(); - } - - //Post deactivating events to containers - for (var i = this.containers.length - 1; i >= 0; i--){ - this.containers[i]._trigger("deactivate", null, this._uiHash(this)); - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", null, this._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - if (this.placeholder) { - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - if(this.placeholder[0].parentNode) { - this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - } - if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) { - this.helper.remove(); - } - - $.extend(this, { - helper: null, - dragging: false, - reverting: false, - _noFinalSort: null - }); - - if(this.domPosition.prev) { - $(this.domPosition.prev).after(this.currentItem); - } else { - $(this.domPosition.parent).prepend(this.currentItem); - } - } - - return this; - - }, - - serialize: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected), - str = []; - o = o || {}; - - $(items).each(function() { - var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/)); - if (res) { - str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2])); - } - }); - - if(!str.length && o.key) { - str.push(o.key + "="); - } - - return str.join("&"); - - }, - - toArray: function(o) { - - var items = this._getItemsAsjQuery(o && o.connected), - ret = []; - - o = o || {}; - - items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); }); - return ret; - - }, - - /* Be careful with the following core functions */ - _intersectsWith: function(item) { - - var x1 = this.positionAbs.left, - x2 = x1 + this.helperProportions.width, - y1 = this.positionAbs.top, - y2 = y1 + this.helperProportions.height, - l = item.left, - r = l + item.width, - t = item.top, - b = t + item.height, - dyClick = this.offset.click.top, - dxClick = this.offset.click.left, - isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r; - - if ( this.options.tolerance === "pointer" || - this.options.forcePointerForContainers || - (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"]) - ) { - return isOverElement; - } else { - - return (l < x1 + (this.helperProportions.width / 2) && // Right Half - x2 - (this.helperProportions.width / 2) < r && // Left Half - t < y1 + (this.helperProportions.height / 2) && // Bottom Half - y2 - (this.helperProportions.height / 2) < b ); // Top Half - - } - }, - - _intersectsWithPointer: function(item) { - - var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height), - isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width), - isOverElement = isOverElementHeight && isOverElementWidth, - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (!isOverElement) { - return false; - } - - return this.floating ? - ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 ) - : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) ); - - }, - - _intersectsWithSides: function(item) { - - var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height), - isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), - verticalDirection = this._getDragVerticalDirection(), - horizontalDirection = this._getDragHorizontalDirection(); - - if (this.floating && horizontalDirection) { - return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf)); - } else { - return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf)); - } - - }, - - _getDragVerticalDirection: function() { - var delta = this.positionAbs.top - this.lastPositionAbs.top; - return delta !== 0 && (delta > 0 ? "down" : "up"); - }, - - _getDragHorizontalDirection: function() { - var delta = this.positionAbs.left - this.lastPositionAbs.left; - return delta !== 0 && (delta > 0 ? "right" : "left"); - }, - - refresh: function(event) { - this._refreshItems(event); - this.refreshPositions(); - return this; - }, - - _connectWith: function() { - var options = this.options; - return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith; - }, - - _getItemsAsjQuery: function(connected) { - - var i, j, cur, inst, - items = [], - queries = [], - connectWith = this._connectWith(); - - if(connectWith && connected) { - for (i = connectWith.length - 1; i >= 0; i--){ - cur = $(connectWith[i]); - for ( j = cur.length - 1; j >= 0; j--){ - inst = $.data(cur[j], this.widgetFullName); - if(inst && inst !== this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]); - } - } - } - } - - queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]); - - for (i = queries.length - 1; i >= 0; i--){ - queries[i][0].each(function() { - items.push(this); - }); - } - - return $(items); - - }, - - _removeCurrentsFromItems: function() { - - var list = this.currentItem.find(":data(" + this.widgetName + "-item)"); - - this.items = $.grep(this.items, function (item) { - for (var j=0; j < list.length; j++) { - if(list[j] === item.item[0]) { - return false; - } - } - return true; - }); - - }, - - _refreshItems: function(event) { - - this.items = []; - this.containers = [this]; - - var i, j, cur, inst, targetData, _queries, item, queriesLength, - items = this.items, - queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]], - connectWith = this._connectWith(); - - if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down - for (i = connectWith.length - 1; i >= 0; i--){ - cur = $(connectWith[i]); - for (j = cur.length - 1; j >= 0; j--){ - inst = $.data(cur[j], this.widgetFullName); - if(inst && inst !== this && !inst.options.disabled) { - queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]); - this.containers.push(inst); - } - } - } - } - - for (i = queries.length - 1; i >= 0; i--) { - targetData = queries[i][1]; - _queries = queries[i][0]; - - for (j=0, queriesLength = _queries.length; j < queriesLength; j++) { - item = $(_queries[j]); - - item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager) - - items.push({ - item: item, - instance: targetData, - width: 0, height: 0, - left: 0, top: 0 - }); - } - } - - }, - - refreshPositions: function(fast) { - - //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change - if(this.offsetParent && this.helper) { - this.offset.parent = this._getParentOffset(); - } - - var i, item, t, p; - - for (i = this.items.length - 1; i >= 0; i--){ - item = this.items[i]; - - //We ignore calculating positions of all connected containers when we're not over them - if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) { - continue; - } - - t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item; - - if (!fast) { - item.width = t.outerWidth(); - item.height = t.outerHeight(); - } - - p = t.offset(); - item.left = p.left; - item.top = p.top; - } - - if(this.options.custom && this.options.custom.refreshContainers) { - this.options.custom.refreshContainers.call(this); - } else { - for (i = this.containers.length - 1; i >= 0; i--){ - p = this.containers[i].element.offset(); - this.containers[i].containerCache.left = p.left; - this.containers[i].containerCache.top = p.top; - this.containers[i].containerCache.width = this.containers[i].element.outerWidth(); - this.containers[i].containerCache.height = this.containers[i].element.outerHeight(); - } - } - - return this; - }, - - _createPlaceholder: function(that) { - that = that || this; - var className, - o = that.options; - - if(!o.placeholder || o.placeholder.constructor === String) { - className = o.placeholder; - o.placeholder = { - element: function() { - - var nodeName = that.currentItem[0].nodeName.toLowerCase(), - element = $( that.document[0].createElement( nodeName ) ) - .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder") - .removeClass("ui-sortable-helper"); - - if ( nodeName === "tr" ) { - // Use a high colspan to force the td to expand the full - // width of the table (browsers are smart enough to - // handle this properly) - element.append( " " ); - } else if ( nodeName === "img" ) { - element.attr( "src", that.currentItem.attr( "src" ) ); - } - - if ( !className ) { - element.css( "visibility", "hidden" ); - } - - return element; - }, - update: function(container, p) { - - // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that - // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified - if(className && !o.forcePlaceholderSize) { - return; - } - - //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item - if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); } - if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); } - } - }; - } - - //Create the placeholder - that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem)); - - //Append it after the actual current item - that.currentItem.after(that.placeholder); - - //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) - o.placeholder.update(that, that.placeholder); - - }, - - _contactContainers: function(event) { - var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating, - innermostContainer = null, - innermostIndex = null; - - // get innermost container that intersects with item - for (i = this.containers.length - 1; i >= 0; i--) { - - // never consider a container that's located within the item itself - if($.contains(this.currentItem[0], this.containers[i].element[0])) { - continue; - } - - if(this._intersectsWith(this.containers[i].containerCache)) { - - // if we've already found a container and it's more "inner" than this, then continue - if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) { - continue; - } - - innermostContainer = this.containers[i]; - innermostIndex = i; - - } else { - // container doesn't intersect. trigger "out" event if necessary - if(this.containers[i].containerCache.over) { - this.containers[i]._trigger("out", event, this._uiHash(this)); - this.containers[i].containerCache.over = 0; - } - } - - } - - // if no intersecting containers found, return - if(!innermostContainer) { - return; - } - - // move the item into the container if it's not there already - if(this.containers.length === 1) { - if (!this.containers[innermostIndex].containerCache.over) { - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } - } else { - - //When entering a new container, we will find the item with the least distance and append our item near it - dist = 10000; - itemWithLeastDistance = null; - floating = innermostContainer.floating || isFloating(this.currentItem); - posProperty = floating ? "left" : "top"; - sizeProperty = floating ? "width" : "height"; - base = this.positionAbs[posProperty] + this.offset.click[posProperty]; - for (j = this.items.length - 1; j >= 0; j--) { - if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) { - continue; - } - if(this.items[j].item[0] === this.currentItem[0]) { - continue; - } - if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) { - continue; - } - cur = this.items[j].item.offset()[posProperty]; - nearBottom = false; - if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){ - nearBottom = true; - cur += this.items[j][sizeProperty]; - } - - if(Math.abs(cur - base) < dist) { - dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j]; - this.direction = nearBottom ? "up": "down"; - } - } - - //Check if dropOnEmpty is enabled - if(!itemWithLeastDistance && !this.options.dropOnEmpty) { - return; - } - - if(this.currentContainer === this.containers[innermostIndex]) { - return; - } - - itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true); - this._trigger("change", event, this._uiHash()); - this.containers[innermostIndex]._trigger("change", event, this._uiHash(this)); - this.currentContainer = this.containers[innermostIndex]; - - //Update the placeholder - this.options.placeholder.update(this.currentContainer, this.placeholder); - - this.containers[innermostIndex]._trigger("over", event, this._uiHash(this)); - this.containers[innermostIndex].containerCache.over = 1; - } - - - }, - - _createHelper: function(event) { - - var o = this.options, - helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem); - - //Add the helper to the DOM if that didn't happen already - if(!helper.parents("body").length) { - $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]); - } - - if(helper[0] === this.currentItem[0]) { - this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") }; - } - - if(!helper[0].style.width || o.forceHelperSize) { - helper.width(this.currentItem.width()); - } - if(!helper[0].style.height || o.forceHelperSize) { - helper.height(this.currentItem.height()); - } - - return helper; - - }, - - _adjustOffsetFromHelper: function(obj) { - if (typeof obj === "string") { - obj = obj.split(" "); - } - if ($.isArray(obj)) { - obj = {left: +obj[0], top: +obj[1] || 0}; - } - if ("left" in obj) { - this.offset.click.left = obj.left + this.margins.left; - } - if ("right" in obj) { - this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; - } - if ("top" in obj) { - this.offset.click.top = obj.top + this.margins.top; - } - if ("bottom" in obj) { - this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; - } - }, - - _getParentOffset: function() { - - - //Get the offsetParent and cache its position - this.offsetParent = this.helper.offsetParent(); - var po = this.offsetParent.offset(); - - // This is a special case where we need to modify a offset calculated on start, since the following happened: - // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent - // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that - // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag - if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) { - po.left += this.scrollParent.scrollLeft(); - po.top += this.scrollParent.scrollTop(); - } - - // This needs to be actually done for all browsers, since pageX/pageY includes this information - // with an ugly IE fix - if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) { - po = { top: 0, left: 0 }; - } - - return { - top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), - left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) - }; - - }, - - _getRelativeOffset: function() { - - if(this.cssPosition === "relative") { - var p = this.currentItem.position(); - return { - top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), - left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() - }; - } else { - return { top: 0, left: 0 }; - } - - }, - - _cacheMargins: function() { - this.margins = { - left: (parseInt(this.currentItem.css("marginLeft"),10) || 0), - top: (parseInt(this.currentItem.css("marginTop"),10) || 0) - }; - }, - - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - - _setContainment: function() { - - var ce, co, over, - o = this.options; - if(o.containment === "parent") { - o.containment = this.helper[0].parentNode; - } - if(o.containment === "document" || o.containment === "window") { - this.containment = [ - 0 - this.offset.relative.left - this.offset.parent.left, - 0 - this.offset.relative.top - this.offset.parent.top, - $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left, - ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top - ]; - } - - if(!(/^(document|window|parent)$/).test(o.containment)) { - ce = $(o.containment)[0]; - co = $(o.containment).offset(); - over = ($(ce).css("overflow") !== "hidden"); - - this.containment = [ - co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left, - co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top, - co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left, - co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - ]; - } - - }, - - _convertPositionTo: function(d, pos) { - - if(!pos) { - pos = this.position; - } - var mod = d === "absolute" ? 1 : -1, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, - scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - return { - top: ( - pos.top + // The absolute mouse position - this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod) - ), - left: ( - pos.left + // The absolute mouse position - this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod) - ) - }; - - }, - - _generatePosition: function(event) { - - var top, left, - o = this.options, - pageX = event.pageX, - pageY = event.pageY, - scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); - - // This is another very weird special case that only happens for relative elements: - // 1. If the css position is relative - // 2. and the scroll parent is the document or similar to the offset parent - // we have to refresh the relative offset during the scroll so there are no jumps - if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) { - this.offset.relative = this._getRelativeOffset(); - } - - /* - * - Position constraining - - * Constrain the position to a mix of grid, containment. - */ - - if(this.originalPosition) { //If we are not dragging yet, we won't check for options - - if(this.containment) { - if(event.pageX - this.offset.click.left < this.containment[0]) { - pageX = this.containment[0] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top < this.containment[1]) { - pageY = this.containment[1] + this.offset.click.top; - } - if(event.pageX - this.offset.click.left > this.containment[2]) { - pageX = this.containment[2] + this.offset.click.left; - } - if(event.pageY - this.offset.click.top > this.containment[3]) { - pageY = this.containment[3] + this.offset.click.top; - } - } - - if(o.grid) { - top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1]; - pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; - - left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0]; - pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; - } - - } - - return { - top: ( - pageY - // The absolute mouse position - this.offset.click.top - // Click offset (relative to the element) - this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.top + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) )) - ), - left: ( - pageX - // The absolute mouse position - this.offset.click.left - // Click offset (relative to the element) - this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent - this.offset.parent.left + // The offsetParent's offset without borders (offset + border) - ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() )) - ) - }; - - }, - - _rearrange: function(event, i, a, hardRefresh) { - - a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling)); - - //Various things done here to improve the performance: - // 1. we create a setTimeout, that calls refreshPositions - // 2. on the instance, we have a counter variable, that get's higher after every append - // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same - // 4. this lets only the last addition to the timeout stack through - this.counter = this.counter ? ++this.counter : 1; - var counter = this.counter; - - this._delay(function() { - if(counter === this.counter) { - this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove - } - }); - - }, - - _clear: function(event, noPropagation) { - - this.reverting = false; - // We delay all events that have to be triggered to after the point where the placeholder has been removed and - // everything else normalized again - var i, - delayedTriggers = []; - - // We first have to update the dom position of the actual currentItem - // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088) - if(!this._noFinalSort && this.currentItem.parent().length) { - this.placeholder.before(this.currentItem); - } - this._noFinalSort = null; - - if(this.helper[0] === this.currentItem[0]) { - for(i in this._storedCSS) { - if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") { - this._storedCSS[i] = ""; - } - } - this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"); - } else { - this.currentItem.show(); - } - - if(this.fromOutside && !noPropagation) { - delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); }); - } - if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) { - delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed - } - - // Check if the items Container has Changed and trigger appropriate - // events. - if (this !== this.currentContainer) { - if(!noPropagation) { - delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); }); - delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); - delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer)); - } - } - - - //Post events to containers - for (i = this.containers.length - 1; i >= 0; i--){ - if(!noPropagation) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - } - if(this.containers[i].containerCache.over) { - delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i])); - this.containers[i].containerCache.over = 0; - } - } - - //Do what was originally in plugins - if ( this.storedCursor ) { - this.document.find( "body" ).css( "cursor", this.storedCursor ); - this.storedStylesheet.remove(); - } - if(this._storedOpacity) { - this.helper.css("opacity", this._storedOpacity); - } - if(this._storedZIndex) { - this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex); - } - - this.dragging = false; - if(this.cancelHelperRemoval) { - if(!noPropagation) { - this._trigger("beforeStop", event, this._uiHash()); - for (i=0; i < delayedTriggers.length; i++) { - delayedTriggers[i].call(this, event); - } //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return false; - } - - if(!noPropagation) { - this._trigger("beforeStop", event, this._uiHash()); - } - - //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node! - this.placeholder[0].parentNode.removeChild(this.placeholder[0]); - - if(this.helper[0] !== this.currentItem[0]) { - this.helper.remove(); - } - this.helper = null; - - if(!noPropagation) { - for (i=0; i < delayedTriggers.length; i++) { - delayedTriggers[i].call(this, event); - } //Trigger all delayed events - this._trigger("stop", event, this._uiHash()); - } - - this.fromOutside = false; - return true; - - }, - - _trigger: function() { - if ($.Widget.prototype._trigger.apply(this, arguments) === false) { - this.cancel(); - } - }, - - _uiHash: function(_inst) { - var inst = _inst || this; - return { - helper: inst.helper, - placeholder: inst.placeholder || $([]), - position: inst.position, - originalPosition: inst.originalPosition, - offset: inst.positionAbs, - item: inst.currentItem, - sender: _inst ? _inst.element : null - }; - } - -}); - -})(jQuery); - -(function($, undefined) { - -var dataSpace = "ui-effects-"; - -$.effects = { - effect: {} -}; - -/*! - * jQuery Color Animations v2.1.2 - * https://github.com/jquery/jquery-color - * - * Copyright 2013 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * Date: Wed Jan 16 08:47:09 2013 -0600 - */ -(function( jQuery, undefined ) { - - var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor", - - // plusequals test for += 100 -= 100 - rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, - // a set of RE's that can match strings and generate color tuples. - stringParsers = [{ - re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - parse: function( execResult ) { - return [ - execResult[ 1 ], - execResult[ 2 ], - execResult[ 3 ], - execResult[ 4 ] - ]; - } - }, { - re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - parse: function( execResult ) { - return [ - execResult[ 1 ] * 2.55, - execResult[ 2 ] * 2.55, - execResult[ 3 ] * 2.55, - execResult[ 4 ] - ]; - } - }, { - // this regex ignores A-F because it's compared against an already lowercased string - re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, - parse: function( execResult ) { - return [ - parseInt( execResult[ 1 ], 16 ), - parseInt( execResult[ 2 ], 16 ), - parseInt( execResult[ 3 ], 16 ) - ]; - } - }, { - // this regex ignores A-F because it's compared against an already lowercased string - re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, - parse: function( execResult ) { - return [ - parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), - parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), - parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) - ]; - } - }, { - re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - space: "hsla", - parse: function( execResult ) { - return [ - execResult[ 1 ], - execResult[ 2 ] / 100, - execResult[ 3 ] / 100, - execResult[ 4 ] - ]; - } - }], - - // jQuery.Color( ) - color = jQuery.Color = function( color, green, blue, alpha ) { - return new jQuery.Color.fn.parse( color, green, blue, alpha ); - }, - spaces = { - rgba: { - props: { - red: { - idx: 0, - type: "byte" - }, - green: { - idx: 1, - type: "byte" - }, - blue: { - idx: 2, - type: "byte" - } - } - }, - - hsla: { - props: { - hue: { - idx: 0, - type: "degrees" - }, - saturation: { - idx: 1, - type: "percent" - }, - lightness: { - idx: 2, - type: "percent" - } - } - } - }, - propTypes = { - "byte": { - floor: true, - max: 255 - }, - "percent": { - max: 1 - }, - "degrees": { - mod: 360, - floor: true - } - }, - support = color.support = {}, - - // element for support tests - supportElem = jQuery( "

" )[ 0 ], - - // colors = jQuery.Color.names - colors, - - // local aliases of functions called often - each = jQuery.each; - -// determine rgba support immediately -supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; -support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1; - -// define cache name and alpha properties -// for rgba and hsla spaces -each( spaces, function( spaceName, space ) { - space.cache = "_" + spaceName; - space.props.alpha = { - idx: 3, - type: "percent", - def: 1 - }; -}); - -function clamp( value, prop, allowEmpty ) { - var type = propTypes[ prop.type ] || {}; - - if ( value == null ) { - return (allowEmpty || !prop.def) ? null : prop.def; - } - - // ~~ is an short way of doing floor for positive numbers - value = type.floor ? ~~value : parseFloat( value ); - - // IE will pass in empty strings as value for alpha, - // which will hit this case - if ( isNaN( value ) ) { - return prop.def; - } - - if ( type.mod ) { - // we add mod before modding to make sure that negatives values - // get converted properly: -10 -> 350 - return (value + type.mod) % type.mod; - } - - // for now all property types without mod have min and max - return 0 > value ? 0 : type.max < value ? type.max : value; -} - -function stringParse( string ) { - var inst = color(), - rgba = inst._rgba = []; - - string = string.toLowerCase(); - - each( stringParsers, function( i, parser ) { - var parsed, - match = parser.re.exec( string ), - values = match && parser.parse( match ), - spaceName = parser.space || "rgba"; - - if ( values ) { - parsed = inst[ spaceName ]( values ); - - // if this was an rgba parse the assignment might happen twice - // oh well.... - inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ]; - rgba = inst._rgba = parsed._rgba; - - // exit each( stringParsers ) here because we matched - return false; - } - }); - - // Found a stringParser that handled it - if ( rgba.length ) { - - // if this came from a parsed string, force "transparent" when alpha is 0 - // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) - if ( rgba.join() === "0,0,0,0" ) { - jQuery.extend( rgba, colors.transparent ); - } - return inst; - } - - // named colors - return colors[ string ]; -} - -color.fn = jQuery.extend( color.prototype, { - parse: function( red, green, blue, alpha ) { - if ( red === undefined ) { - this._rgba = [ null, null, null, null ]; - return this; - } - if ( red.jquery || red.nodeType ) { - red = jQuery( red ).css( green ); - green = undefined; - } - - var inst = this, - type = jQuery.type( red ), - rgba = this._rgba = []; - - // more than 1 argument specified - assume ( red, green, blue, alpha ) - if ( green !== undefined ) { - red = [ red, green, blue, alpha ]; - type = "array"; - } - - if ( type === "string" ) { - return this.parse( stringParse( red ) || colors._default ); - } - - if ( type === "array" ) { - each( spaces.rgba.props, function( key, prop ) { - rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); - }); - return this; - } - - if ( type === "object" ) { - if ( red instanceof color ) { - each( spaces, function( spaceName, space ) { - if ( red[ space.cache ] ) { - inst[ space.cache ] = red[ space.cache ].slice(); - } - }); - } else { - each( spaces, function( spaceName, space ) { - var cache = space.cache; - each( space.props, function( key, prop ) { - - // if the cache doesn't exist, and we know how to convert - if ( !inst[ cache ] && space.to ) { - - // if the value was null, we don't need to copy it - // if the key was alpha, we don't need to copy it either - if ( key === "alpha" || red[ key ] == null ) { - return; - } - inst[ cache ] = space.to( inst._rgba ); - } - - // this is the only case where we allow nulls for ALL properties. - // call clamp with alwaysAllowEmpty - inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); - }); - - // everything defined but alpha? - if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) { - // use the default of 1 - inst[ cache ][ 3 ] = 1; - if ( space.from ) { - inst._rgba = space.from( inst[ cache ] ); - } - } - }); - } - return this; - } - }, - is: function( compare ) { - var is = color( compare ), - same = true, - inst = this; - - each( spaces, function( _, space ) { - var localCache, - isCache = is[ space.cache ]; - if (isCache) { - localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || []; - each( space.props, function( _, prop ) { - if ( isCache[ prop.idx ] != null ) { - same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); - return same; - } - }); - } - return same; - }); - return same; - }, - _space: function() { - var used = [], - inst = this; - each( spaces, function( spaceName, space ) { - if ( inst[ space.cache ] ) { - used.push( spaceName ); - } - }); - return used.pop(); - }, - transition: function( other, distance ) { - var end = color( other ), - spaceName = end._space(), - space = spaces[ spaceName ], - startColor = this.alpha() === 0 ? color( "transparent" ) : this, - start = startColor[ space.cache ] || space.to( startColor._rgba ), - result = start.slice(); - - end = end[ space.cache ]; - each( space.props, function( key, prop ) { - var index = prop.idx, - startValue = start[ index ], - endValue = end[ index ], - type = propTypes[ prop.type ] || {}; - - // if null, don't override start value - if ( endValue === null ) { - return; - } - // if null - use end - if ( startValue === null ) { - result[ index ] = endValue; - } else { - if ( type.mod ) { - if ( endValue - startValue > type.mod / 2 ) { - startValue += type.mod; - } else if ( startValue - endValue > type.mod / 2 ) { - startValue -= type.mod; - } - } - result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); - } - }); - return this[ spaceName ]( result ); - }, - blend: function( opaque ) { - // if we are already opaque - return ourself - if ( this._rgba[ 3 ] === 1 ) { - return this; - } - - var rgb = this._rgba.slice(), - a = rgb.pop(), - blend = color( opaque )._rgba; - - return color( jQuery.map( rgb, function( v, i ) { - return ( 1 - a ) * blend[ i ] + a * v; - })); - }, - toRgbaString: function() { - var prefix = "rgba(", - rgba = jQuery.map( this._rgba, function( v, i ) { - return v == null ? ( i > 2 ? 1 : 0 ) : v; - }); - - if ( rgba[ 3 ] === 1 ) { - rgba.pop(); - prefix = "rgb("; - } - - return prefix + rgba.join() + ")"; - }, - toHslaString: function() { - var prefix = "hsla(", - hsla = jQuery.map( this.hsla(), function( v, i ) { - if ( v == null ) { - v = i > 2 ? 1 : 0; - } - - // catch 1 and 2 - if ( i && i < 3 ) { - v = Math.round( v * 100 ) + "%"; - } - return v; - }); - - if ( hsla[ 3 ] === 1 ) { - hsla.pop(); - prefix = "hsl("; - } - return prefix + hsla.join() + ")"; - }, - toHexString: function( includeAlpha ) { - var rgba = this._rgba.slice(), - alpha = rgba.pop(); - - if ( includeAlpha ) { - rgba.push( ~~( alpha * 255 ) ); - } - - return "#" + jQuery.map( rgba, function( v ) { - - // default to 0 when nulls exist - v = ( v || 0 ).toString( 16 ); - return v.length === 1 ? "0" + v : v; - }).join(""); - }, - toString: function() { - return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); - } -}); -color.fn.parse.prototype = color.fn; - -// hsla conversions adapted from: -// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021 - -function hue2rgb( p, q, h ) { - h = ( h + 1 ) % 1; - if ( h * 6 < 1 ) { - return p + (q - p) * h * 6; - } - if ( h * 2 < 1) { - return q; - } - if ( h * 3 < 2 ) { - return p + (q - p) * ((2/3) - h) * 6; - } - return p; -} - -spaces.hsla.to = function ( rgba ) { - if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { - return [ null, null, null, rgba[ 3 ] ]; - } - var r = rgba[ 0 ] / 255, - g = rgba[ 1 ] / 255, - b = rgba[ 2 ] / 255, - a = rgba[ 3 ], - max = Math.max( r, g, b ), - min = Math.min( r, g, b ), - diff = max - min, - add = max + min, - l = add * 0.5, - h, s; - - if ( min === max ) { - h = 0; - } else if ( r === max ) { - h = ( 60 * ( g - b ) / diff ) + 360; - } else if ( g === max ) { - h = ( 60 * ( b - r ) / diff ) + 120; - } else { - h = ( 60 * ( r - g ) / diff ) + 240; - } - - // chroma (diff) == 0 means greyscale which, by definition, saturation = 0% - // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) - if ( diff === 0 ) { - s = 0; - } else if ( l <= 0.5 ) { - s = diff / add; - } else { - s = diff / ( 2 - add ); - } - return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; -}; - -spaces.hsla.from = function ( hsla ) { - if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { - return [ null, null, null, hsla[ 3 ] ]; - } - var h = hsla[ 0 ] / 360, - s = hsla[ 1 ], - l = hsla[ 2 ], - a = hsla[ 3 ], - q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, - p = 2 * l - q; - - return [ - Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), - Math.round( hue2rgb( p, q, h ) * 255 ), - Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), - a - ]; -}; - - -each( spaces, function( spaceName, space ) { - var props = space.props, - cache = space.cache, - to = space.to, - from = space.from; - - // makes rgba() and hsla() - color.fn[ spaceName ] = function( value ) { - - // generate a cache for this space if it doesn't exist - if ( to && !this[ cache ] ) { - this[ cache ] = to( this._rgba ); - } - if ( value === undefined ) { - return this[ cache ].slice(); - } - - var ret, - type = jQuery.type( value ), - arr = ( type === "array" || type === "object" ) ? value : arguments, - local = this[ cache ].slice(); - - each( props, function( key, prop ) { - var val = arr[ type === "object" ? key : prop.idx ]; - if ( val == null ) { - val = local[ prop.idx ]; - } - local[ prop.idx ] = clamp( val, prop ); - }); - - if ( from ) { - ret = color( from( local ) ); - ret[ cache ] = local; - return ret; - } else { - return color( local ); - } - }; - - // makes red() green() blue() alpha() hue() saturation() lightness() - each( props, function( key, prop ) { - // alpha is included in more than one space - if ( color.fn[ key ] ) { - return; - } - color.fn[ key ] = function( value ) { - var vtype = jQuery.type( value ), - fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ), - local = this[ fn ](), - cur = local[ prop.idx ], - match; - - if ( vtype === "undefined" ) { - return cur; - } - - if ( vtype === "function" ) { - value = value.call( this, cur ); - vtype = jQuery.type( value ); - } - if ( value == null && prop.empty ) { - return this; - } - if ( vtype === "string" ) { - match = rplusequals.exec( value ); - if ( match ) { - value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); - } - } - local[ prop.idx ] = value; - return this[ fn ]( local ); - }; - }); -}); - -// add cssHook and .fx.step function for each named hook. -// accept a space separated string of properties -color.hook = function( hook ) { - var hooks = hook.split( " " ); - each( hooks, function( i, hook ) { - jQuery.cssHooks[ hook ] = { - set: function( elem, value ) { - var parsed, curElem, - backgroundColor = ""; - - if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) { - value = color( parsed || value ); - if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { - curElem = hook === "backgroundColor" ? elem.parentNode : elem; - while ( - (backgroundColor === "" || backgroundColor === "transparent") && - curElem && curElem.style - ) { - try { - backgroundColor = jQuery.css( curElem, "backgroundColor" ); - curElem = curElem.parentNode; - } catch ( e ) { - } - } - - value = value.blend( backgroundColor && backgroundColor !== "transparent" ? - backgroundColor : - "_default" ); - } - - value = value.toRgbaString(); - } - try { - elem.style[ hook ] = value; - } catch( e ) { - // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' - } - } - }; - jQuery.fx.step[ hook ] = function( fx ) { - if ( !fx.colorInit ) { - fx.start = color( fx.elem, hook ); - fx.end = color( fx.end ); - fx.colorInit = true; - } - jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); - }; - }); - -}; - -color.hook( stepHooks ); - -jQuery.cssHooks.borderColor = { - expand: function( value ) { - var expanded = {}; - - each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) { - expanded[ "border" + part + "Color" ] = value; - }); - return expanded; - } -}; - -// Basic color names only. -// Usage of any of the other color names requires adding yourself or including -// jquery.color.svg-names.js. -colors = jQuery.Color.names = { - // 4.1. Basic color keywords - aqua: "#00ffff", - black: "#000000", - blue: "#0000ff", - fuchsia: "#ff00ff", - gray: "#808080", - green: "#008000", - lime: "#00ff00", - maroon: "#800000", - navy: "#000080", - olive: "#808000", - purple: "#800080", - red: "#ff0000", - silver: "#c0c0c0", - teal: "#008080", - white: "#ffffff", - yellow: "#ffff00", - - // 4.2.3. "transparent" color keyword - transparent: [ null, null, null, 0 ], - - _default: "#ffffff" -}; - -})( jQuery ); - - -/******************************************************************************/ -/****************************** CLASS ANIMATIONS ******************************/ -/******************************************************************************/ -(function() { - -var classAnimationActions = [ "add", "remove", "toggle" ], - shorthandStyles = { - border: 1, - borderBottom: 1, - borderColor: 1, - borderLeft: 1, - borderRight: 1, - borderTop: 1, - borderWidth: 1, - margin: 1, - padding: 1 - }; - -$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) { - $.fx.step[ prop ] = function( fx ) { - if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { - jQuery.style( fx.elem, prop, fx.end ); - fx.setAttr = true; - } - }; -}); - -function getElementStyles( elem ) { - var key, len, - style = elem.ownerDocument.defaultView ? - elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : - elem.currentStyle, - styles = {}; - - if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { - len = style.length; - while ( len-- ) { - key = style[ len ]; - if ( typeof style[ key ] === "string" ) { - styles[ $.camelCase( key ) ] = style[ key ]; - } - } - // support: Opera, IE <9 - } else { - for ( key in style ) { - if ( typeof style[ key ] === "string" ) { - styles[ key ] = style[ key ]; - } - } - } - - return styles; -} - - -function styleDifference( oldStyle, newStyle ) { - var diff = {}, - name, value; - - for ( name in newStyle ) { - value = newStyle[ name ]; - if ( oldStyle[ name ] !== value ) { - if ( !shorthandStyles[ name ] ) { - if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { - diff[ name ] = value; - } - } - } - } - - return diff; -} - -// support: jQuery <1.8 -if ( !$.fn.addBack ) { - $.fn.addBack = function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - }; -} - -$.effects.animateClass = function( value, duration, easing, callback ) { - var o = $.speed( duration, easing, callback ); - - return this.queue( function() { - var animated = $( this ), - baseClass = animated.attr( "class" ) || "", - applyClassChange, - allAnimations = o.children ? animated.find( "*" ).addBack() : animated; - - // map the animated objects to store the original styles. - allAnimations = allAnimations.map(function() { - var el = $( this ); - return { - el: el, - start: getElementStyles( this ) - }; - }); - - // apply class change - applyClassChange = function() { - $.each( classAnimationActions, function(i, action) { - if ( value[ action ] ) { - animated[ action + "Class" ]( value[ action ] ); - } - }); - }; - applyClassChange(); - - // map all animated objects again - calculate new styles and diff - allAnimations = allAnimations.map(function() { - this.end = getElementStyles( this.el[ 0 ] ); - this.diff = styleDifference( this.start, this.end ); - return this; - }); - - // apply original class - animated.attr( "class", baseClass ); - - // map all animated objects again - this time collecting a promise - allAnimations = allAnimations.map(function() { - var styleInfo = this, - dfd = $.Deferred(), - opts = $.extend({}, o, { - queue: false, - complete: function() { - dfd.resolve( styleInfo ); - } - }); - - this.el.animate( this.diff, opts ); - return dfd.promise(); - }); - - // once all animations have completed: - $.when.apply( $, allAnimations.get() ).done(function() { - - // set the final class - applyClassChange(); - - // for each animated element, - // clear all css properties that were animated - $.each( arguments, function() { - var el = this.el; - $.each( this.diff, function(key) { - el.css( key, "" ); - }); - }); - - // this is guarnteed to be there if you use jQuery.speed() - // it also handles dequeuing the next anim... - o.complete.call( animated[ 0 ] ); - }); - }); -}; - -$.fn.extend({ - addClass: (function( orig ) { - return function( classNames, speed, easing, callback ) { - return speed ? - $.effects.animateClass.call( this, - { add: classNames }, speed, easing, callback ) : - orig.apply( this, arguments ); - }; - })( $.fn.addClass ), - - removeClass: (function( orig ) { - return function( classNames, speed, easing, callback ) { - return arguments.length > 1 ? - $.effects.animateClass.call( this, - { remove: classNames }, speed, easing, callback ) : - orig.apply( this, arguments ); - }; - })( $.fn.removeClass ), - - toggleClass: (function( orig ) { - return function( classNames, force, speed, easing, callback ) { - if ( typeof force === "boolean" || force === undefined ) { - if ( !speed ) { - // without speed parameter - return orig.apply( this, arguments ); - } else { - return $.effects.animateClass.call( this, - (force ? { add: classNames } : { remove: classNames }), - speed, easing, callback ); - } - } else { - // without force parameter - return $.effects.animateClass.call( this, - { toggle: classNames }, force, speed, easing ); - } - }; - })( $.fn.toggleClass ), - - switchClass: function( remove, add, speed, easing, callback) { - return $.effects.animateClass.call( this, { - add: add, - remove: remove - }, speed, easing, callback ); - } -}); - -})(); - -/******************************************************************************/ -/*********************************** EFFECTS **********************************/ -/******************************************************************************/ - -(function() { - -$.extend( $.effects, { - version: "1.10.2", - - // Saves a set of properties in a data storage - save: function( element, set ) { - for( var i=0; i < set.length; i++ ) { - if ( set[ i ] !== null ) { - element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); - } - } - }, - - // Restores a set of previously saved properties from a data storage - restore: function( element, set ) { - var val, i; - for( i=0; i < set.length; i++ ) { - if ( set[ i ] !== null ) { - val = element.data( dataSpace + set[ i ] ); - // support: jQuery 1.6.2 - // http://bugs.jquery.com/ticket/9917 - // jQuery 1.6.2 incorrectly returns undefined for any falsy value. - // We can't differentiate between "" and 0 here, so we just assume - // empty string since it's likely to be a more common value... - if ( val === undefined ) { - val = ""; - } - element.css( set[ i ], val ); - } - } - }, - - setMode: function( el, mode ) { - if (mode === "toggle") { - mode = el.is( ":hidden" ) ? "show" : "hide"; - } - return mode; - }, - - // Translates a [top,left] array into a baseline value - // this should be a little more flexible in the future to handle a string & hash - getBaseline: function( origin, original ) { - var y, x; - switch ( origin[ 0 ] ) { - case "top": y = 0; break; - case "middle": y = 0.5; break; - case "bottom": y = 1; break; - default: y = origin[ 0 ] / original.height; - } - switch ( origin[ 1 ] ) { - case "left": x = 0; break; - case "center": x = 0.5; break; - case "right": x = 1; break; - default: x = origin[ 1 ] / original.width; - } - return { - x: x, - y: y - }; - }, - - // Wraps the element around a wrapper that copies position properties - createWrapper: function( element ) { - - // if the element is already wrapped, return it - if ( element.parent().is( ".ui-effects-wrapper" )) { - return element.parent(); - } - - // wrap the element - var props = { - width: element.outerWidth(true), - height: element.outerHeight(true), - "float": element.css( "float" ) - }, - wrapper = $( "

" ) - .addClass( "ui-effects-wrapper" ) - .css({ - fontSize: "100%", - background: "transparent", - border: "none", - margin: 0, - padding: 0 - }), - // Store the size in case width/height are defined in % - Fixes #5245 - size = { - width: element.width(), - height: element.height() - }, - active = document.activeElement; - - // support: Firefox - // Firefox incorrectly exposes anonymous content - // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 - try { - active.id; - } catch( e ) { - active = document.body; - } - - element.wrap( wrapper ); - - // Fixes #7595 - Elements lose focus when wrapped. - if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { - $( active ).focus(); - } - - wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element - - // transfer positioning properties to the wrapper - if ( element.css( "position" ) === "static" ) { - wrapper.css({ position: "relative" }); - element.css({ position: "relative" }); - } else { - $.extend( props, { - position: element.css( "position" ), - zIndex: element.css( "z-index" ) - }); - $.each([ "top", "left", "bottom", "right" ], function(i, pos) { - props[ pos ] = element.css( pos ); - if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { - props[ pos ] = "auto"; - } - }); - element.css({ - position: "relative", - top: 0, - left: 0, - right: "auto", - bottom: "auto" - }); - } - element.css(size); - - return wrapper.css( props ).show(); - }, - - removeWrapper: function( element ) { - var active = document.activeElement; - - if ( element.parent().is( ".ui-effects-wrapper" ) ) { - element.parent().replaceWith( element ); - - // Fixes #7595 - Elements lose focus when wrapped. - if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { - $( active ).focus(); - } - } - - - return element; - }, - - setTransition: function( element, list, factor, value ) { - value = value || {}; - $.each( list, function( i, x ) { - var unit = element.cssUnit( x ); - if ( unit[ 0 ] > 0 ) { - value[ x ] = unit[ 0 ] * factor + unit[ 1 ]; - } - }); - return value; - } -}); - -// return an effect options object for the given parameters: -function _normalizeArguments( effect, options, speed, callback ) { - - // allow passing all options as the first parameter - if ( $.isPlainObject( effect ) ) { - options = effect; - effect = effect.effect; - } - - // convert to an object - effect = { effect: effect }; - - // catch (effect, null, ...) - if ( options == null ) { - options = {}; - } - - // catch (effect, callback) - if ( $.isFunction( options ) ) { - callback = options; - speed = null; - options = {}; - } - - // catch (effect, speed, ?) - if ( typeof options === "number" || $.fx.speeds[ options ] ) { - callback = speed; - speed = options; - options = {}; - } - - // catch (effect, options, callback) - if ( $.isFunction( speed ) ) { - callback = speed; - speed = null; - } - - // add options to effect - if ( options ) { - $.extend( effect, options ); - } - - speed = speed || options.duration; - effect.duration = $.fx.off ? 0 : - typeof speed === "number" ? speed : - speed in $.fx.speeds ? $.fx.speeds[ speed ] : - $.fx.speeds._default; - - effect.complete = callback || options.complete; - - return effect; -} - -function standardAnimationOption( option ) { - // Valid standard speeds (nothing, number, named speed) - if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) { - return true; - } - - // Invalid strings - treat as "normal" speed - if ( typeof option === "string" && !$.effects.effect[ option ] ) { - return true; - } - - // Complete callback - if ( $.isFunction( option ) ) { - return true; - } - - // Options hash (but not naming an effect) - if ( typeof option === "object" && !option.effect ) { - return true; - } - - // Didn't match any standard API - return false; -} - -$.fn.extend({ - effect: function( /* effect, options, speed, callback */ ) { - var args = _normalizeArguments.apply( this, arguments ), - mode = args.mode, - queue = args.queue, - effectMethod = $.effects.effect[ args.effect ]; - - if ( $.fx.off || !effectMethod ) { - // delegate to the original method (e.g., .show()) if possible - if ( mode ) { - return this[ mode ]( args.duration, args.complete ); - } else { - return this.each( function() { - if ( args.complete ) { - args.complete.call( this ); - } - }); - } - } - - function run( next ) { - var elem = $( this ), - complete = args.complete, - mode = args.mode; - - function done() { - if ( $.isFunction( complete ) ) { - complete.call( elem[0] ); - } - if ( $.isFunction( next ) ) { - next(); - } - } - - // If the element already has the correct final state, delegate to - // the core methods so the internal tracking of "olddisplay" works. - if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { - elem[ mode ](); - done(); - } else { - effectMethod.call( elem[0], args, done ); - } - } - - return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); - }, - - show: (function( orig ) { - return function( option ) { - if ( standardAnimationOption( option ) ) { - return orig.apply( this, arguments ); - } else { - var args = _normalizeArguments.apply( this, arguments ); - args.mode = "show"; - return this.effect.call( this, args ); - } - }; - })( $.fn.show ), - - hide: (function( orig ) { - return function( option ) { - if ( standardAnimationOption( option ) ) { - return orig.apply( this, arguments ); - } else { - var args = _normalizeArguments.apply( this, arguments ); - args.mode = "hide"; - return this.effect.call( this, args ); - } - }; - })( $.fn.hide ), - - toggle: (function( orig ) { - return function( option ) { - if ( standardAnimationOption( option ) || typeof option === "boolean" ) { - return orig.apply( this, arguments ); - } else { - var args = _normalizeArguments.apply( this, arguments ); - args.mode = "toggle"; - return this.effect.call( this, args ); - } - }; - })( $.fn.toggle ), - - // helper functions - cssUnit: function(key) { - var style = this.css( key ), - val = []; - - $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { - if ( style.indexOf( unit ) > 0 ) { - val = [ parseFloat( style ), unit ]; - } - }); - return val; - } -}); - -})(); - -/******************************************************************************/ -/*********************************** EASING ***********************************/ -/******************************************************************************/ - -(function() { - -// based on easing equations from Robert Penner (http://www.robertpenner.com/easing) - -var baseEasings = {}; - -$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { - baseEasings[ name ] = function( p ) { - return Math.pow( p, i + 2 ); - }; -}); - -$.extend( baseEasings, { - Sine: function ( p ) { - return 1 - Math.cos( p * Math.PI / 2 ); - }, - Circ: function ( p ) { - return 1 - Math.sqrt( 1 - p * p ); - }, - Elastic: function( p ) { - return p === 0 || p === 1 ? p : - -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 ); - }, - Back: function( p ) { - return p * p * ( 3 * p - 2 ); - }, - Bounce: function ( p ) { - var pow2, - bounce = 4; - - while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} - return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); - } -}); - -$.each( baseEasings, function( name, easeIn ) { - $.easing[ "easeIn" + name ] = easeIn; - $.easing[ "easeOut" + name ] = function( p ) { - return 1 - easeIn( 1 - p ); - }; - $.easing[ "easeInOut" + name ] = function( p ) { - return p < 0.5 ? - easeIn( p * 2 ) / 2 : - 1 - easeIn( p * -2 + 2 ) / 2; - }; -}); - -})(); - -})(jQuery); - -(function( $, undefined ) { - -var uid = 0, - hideProps = {}, - showProps = {}; - -hideProps.height = hideProps.paddingTop = hideProps.paddingBottom = - hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide"; -showProps.height = showProps.paddingTop = showProps.paddingBottom = - showProps.borderTopWidth = showProps.borderBottomWidth = "show"; - -$.widget( "ui.accordion", { - version: "1.10.2", - options: { - active: 0, - animate: {}, - collapsible: false, - event: "click", - header: "> li > :first-child,> :not(li):even", - heightStyle: "auto", - icons: { - activeHeader: "ui-icon-triangle-1-s", - header: "ui-icon-triangle-1-e" - }, - - // callbacks - activate: null, - beforeActivate: null - }, - - _create: function() { - var options = this.options; - this.prevShow = this.prevHide = $(); - this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) - // ARIA - .attr( "role", "tablist" ); - - // don't allow collapsible: false and active: false / null - if ( !options.collapsible && (options.active === false || options.active == null) ) { - options.active = 0; - } - - this._processPanels(); - // handle negative values - if ( options.active < 0 ) { - options.active += this.headers.length; - } - this._refresh(); - }, - - _getCreateEventData: function() { - return { - header: this.active, - panel: !this.active.length ? $() : this.active.next(), - content: !this.active.length ? $() : this.active.next() - }; - }, - - _createIcons: function() { - var icons = this.options.icons; - if ( icons ) { - $( "" ) - .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) - .prependTo( this.headers ); - this.active.children( ".ui-accordion-header-icon" ) - .removeClass( icons.header ) - .addClass( icons.activeHeader ); - this.headers.addClass( "ui-accordion-icons" ); - } - }, - - _destroyIcons: function() { - this.headers - .removeClass( "ui-accordion-icons" ) - .children( ".ui-accordion-header-icon" ) - .remove(); - }, - - _destroy: function() { - var contents; - - // clean up main element - this.element - .removeClass( "ui-accordion ui-widget ui-helper-reset" ) - .removeAttr( "role" ); - - // clean up headers - this.headers - .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) - .removeAttr( "role" ) - .removeAttr( "aria-selected" ) - .removeAttr( "aria-controls" ) - .removeAttr( "tabIndex" ) - .each(function() { - if ( /^ui-accordion/.test( this.id ) ) { - this.removeAttribute( "id" ); - } - }); - this._destroyIcons(); - - // clean up content panels - contents = this.headers.next() - .css( "display", "" ) - .removeAttr( "role" ) - .removeAttr( "aria-expanded" ) - .removeAttr( "aria-hidden" ) - .removeAttr( "aria-labelledby" ) - .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" ) - .each(function() { - if ( /^ui-accordion/.test( this.id ) ) { - this.removeAttribute( "id" ); - } - }); - if ( this.options.heightStyle !== "content" ) { - contents.css( "height", "" ); - } - }, - - _setOption: function( key, value ) { - if ( key === "active" ) { - // _activate() will handle invalid values and update this.options - this._activate( value ); - return; - } - - if ( key === "event" ) { - if ( this.options.event ) { - this._off( this.headers, this.options.event ); - } - this._setupEvents( value ); - } - - this._super( key, value ); - - // setting collapsible: false while collapsed; open first panel - if ( key === "collapsible" && !value && this.options.active === false ) { - this._activate( 0 ); - } - - if ( key === "icons" ) { - this._destroyIcons(); - if ( value ) { - this._createIcons(); - } - } - - // #5332 - opacity doesn't cascade to positioned elements in IE - // so we need to add the disabled class to the headers and panels - if ( key === "disabled" ) { - this.headers.add( this.headers.next() ) - .toggleClass( "ui-state-disabled", !!value ); - } - }, - - _keydown: function( event ) { - /*jshint maxcomplexity:15*/ - if ( event.altKey || event.ctrlKey ) { - return; - } - - var keyCode = $.ui.keyCode, - length = this.headers.length, - currentIndex = this.headers.index( event.target ), - toFocus = false; - - switch ( event.keyCode ) { - case keyCode.RIGHT: - case keyCode.DOWN: - toFocus = this.headers[ ( currentIndex + 1 ) % length ]; - break; - case keyCode.LEFT: - case keyCode.UP: - toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; - break; - case keyCode.SPACE: - case keyCode.ENTER: - this._eventHandler( event ); - break; - case keyCode.HOME: - toFocus = this.headers[ 0 ]; - break; - case keyCode.END: - toFocus = this.headers[ length - 1 ]; - break; - } - - if ( toFocus ) { - $( event.target ).attr( "tabIndex", -1 ); - $( toFocus ).attr( "tabIndex", 0 ); - toFocus.focus(); - event.preventDefault(); - } - }, - - _panelKeyDown : function( event ) { - if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { - $( event.currentTarget ).prev().focus(); - } - }, - - refresh: function() { - var options = this.options; - this._processPanels(); - - // was collapsed or no panel - if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { - options.active = false; - this.active = $(); - // active false only when collapsible is true - } if ( options.active === false ) { - this._activate( 0 ); - // was active, but active panel is gone - } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { - // all remaining panel are disabled - if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { - options.active = false; - this.active = $(); - // activate previous panel - } else { - this._activate( Math.max( 0, options.active - 1 ) ); - } - // was active, active panel still exists - } else { - // make sure active index is correct - options.active = this.headers.index( this.active ); - } - - this._destroyIcons(); - - this._refresh(); - }, - - _processPanels: function() { - this.headers = this.element.find( this.options.header ) - .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ); - - this.headers.next() - .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) - .filter(":not(.ui-accordion-content-active)") - .hide(); - }, - - _refresh: function() { - var maxHeight, - options = this.options, - heightStyle = options.heightStyle, - parent = this.element.parent(), - accordionId = this.accordionId = "ui-accordion-" + - (this.element.attr( "id" ) || ++uid); - - this.active = this._findActive( options.active ) - .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) - .removeClass( "ui-corner-all" ); - this.active.next() - .addClass( "ui-accordion-content-active" ) - .show(); - - this.headers - .attr( "role", "tab" ) - .each(function( i ) { - var header = $( this ), - headerId = header.attr( "id" ), - panel = header.next(), - panelId = panel.attr( "id" ); - if ( !headerId ) { - headerId = accordionId + "-header-" + i; - header.attr( "id", headerId ); - } - if ( !panelId ) { - panelId = accordionId + "-panel-" + i; - panel.attr( "id", panelId ); - } - header.attr( "aria-controls", panelId ); - panel.attr( "aria-labelledby", headerId ); - }) - .next() - .attr( "role", "tabpanel" ); - - this.headers - .not( this.active ) - .attr({ - "aria-selected": "false", - tabIndex: -1 - }) - .next() - .attr({ - "aria-expanded": "false", - "aria-hidden": "true" - }) - .hide(); - - // make sure at least one header is in the tab order - if ( !this.active.length ) { - this.headers.eq( 0 ).attr( "tabIndex", 0 ); - } else { - this.active.attr({ - "aria-selected": "true", - tabIndex: 0 - }) - .next() - .attr({ - "aria-expanded": "true", - "aria-hidden": "false" - }); - } - - this._createIcons(); - - this._setupEvents( options.event ); - - if ( heightStyle === "fill" ) { - maxHeight = parent.height(); - this.element.siblings( ":visible" ).each(function() { - var elem = $( this ), - position = elem.css( "position" ); - - if ( position === "absolute" || position === "fixed" ) { - return; - } - maxHeight -= elem.outerHeight( true ); - }); - - this.headers.each(function() { - maxHeight -= $( this ).outerHeight( true ); - }); - - this.headers.next() - .each(function() { - $( this ).height( Math.max( 0, maxHeight - - $( this ).innerHeight() + $( this ).height() ) ); - }) - .css( "overflow", "auto" ); - } else if ( heightStyle === "auto" ) { - maxHeight = 0; - this.headers.next() - .each(function() { - maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); - }) - .height( maxHeight ); - } - }, - - _activate: function( index ) { - var active = this._findActive( index )[ 0 ]; - - // trying to activate the already active panel - if ( active === this.active[ 0 ] ) { - return; - } - - // trying to collapse, simulate a click on the currently active header - active = active || this.active[ 0 ]; - - this._eventHandler({ - target: active, - currentTarget: active, - preventDefault: $.noop - }); - }, - - _findActive: function( selector ) { - return typeof selector === "number" ? this.headers.eq( selector ) : $(); - }, - - _setupEvents: function( event ) { - var events = { - keydown: "_keydown" - }; - if ( event ) { - $.each( event.split(" "), function( index, eventName ) { - events[ eventName ] = "_eventHandler"; - }); - } - - this._off( this.headers.add( this.headers.next() ) ); - this._on( this.headers, events ); - this._on( this.headers.next(), { keydown: "_panelKeyDown" }); - this._hoverable( this.headers ); - this._focusable( this.headers ); - }, - - _eventHandler: function( event ) { - var options = this.options, - active = this.active, - clicked = $( event.currentTarget ), - clickedIsActive = clicked[ 0 ] === active[ 0 ], - collapsing = clickedIsActive && options.collapsible, - toShow = collapsing ? $() : clicked.next(), - toHide = active.next(), - eventData = { - oldHeader: active, - oldPanel: toHide, - newHeader: collapsing ? $() : clicked, - newPanel: toShow - }; - - event.preventDefault(); - - if ( - // click on active header, but not collapsible - ( clickedIsActive && !options.collapsible ) || - // allow canceling activation - ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { - return; - } - - options.active = collapsing ? false : this.headers.index( clicked ); - - // when the call to ._toggle() comes after the class changes - // it causes a very odd bug in IE 8 (see #6720) - this.active = clickedIsActive ? $() : clicked; - this._toggle( eventData ); - - // switch classes - // corner classes on the previously active header stay after the animation - active.removeClass( "ui-accordion-header-active ui-state-active" ); - if ( options.icons ) { - active.children( ".ui-accordion-header-icon" ) - .removeClass( options.icons.activeHeader ) - .addClass( options.icons.header ); - } - - if ( !clickedIsActive ) { - clicked - .removeClass( "ui-corner-all" ) - .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); - if ( options.icons ) { - clicked.children( ".ui-accordion-header-icon" ) - .removeClass( options.icons.header ) - .addClass( options.icons.activeHeader ); - } - - clicked - .next() - .addClass( "ui-accordion-content-active" ); - } - }, - - _toggle: function( data ) { - var toShow = data.newPanel, - toHide = this.prevShow.length ? this.prevShow : data.oldPanel; - - // handle activating a panel during the animation for another activation - this.prevShow.add( this.prevHide ).stop( true, true ); - this.prevShow = toShow; - this.prevHide = toHide; - - if ( this.options.animate ) { - this._animate( toShow, toHide, data ); - } else { - toHide.hide(); - toShow.show(); - this._toggleComplete( data ); - } - - toHide.attr({ - "aria-expanded": "false", - "aria-hidden": "true" - }); - toHide.prev().attr( "aria-selected", "false" ); - // if we're switching panels, remove the old header from the tab order - // if we're opening from collapsed state, remove the previous header from the tab order - // if we're collapsing, then keep the collapsing header in the tab order - if ( toShow.length && toHide.length ) { - toHide.prev().attr( "tabIndex", -1 ); - } else if ( toShow.length ) { - this.headers.filter(function() { - return $( this ).attr( "tabIndex" ) === 0; - }) - .attr( "tabIndex", -1 ); - } - - toShow - .attr({ - "aria-expanded": "true", - "aria-hidden": "false" - }) - .prev() - .attr({ - "aria-selected": "true", - tabIndex: 0 - }); - }, - - _animate: function( toShow, toHide, data ) { - var total, easing, duration, - that = this, - adjust = 0, - down = toShow.length && - ( !toHide.length || ( toShow.index() < toHide.index() ) ), - animate = this.options.animate || {}, - options = down && animate.down || animate, - complete = function() { - that._toggleComplete( data ); - }; - - if ( typeof options === "number" ) { - duration = options; - } - if ( typeof options === "string" ) { - easing = options; - } - // fall back from options to animation in case of partial down settings - easing = easing || options.easing || animate.easing; - duration = duration || options.duration || animate.duration; - - if ( !toHide.length ) { - return toShow.animate( showProps, duration, easing, complete ); - } - if ( !toShow.length ) { - return toHide.animate( hideProps, duration, easing, complete ); - } - - total = toShow.show().outerHeight(); - toHide.animate( hideProps, { - duration: duration, - easing: easing, - step: function( now, fx ) { - fx.now = Math.round( now ); - } - }); - toShow - .hide() - .animate( showProps, { - duration: duration, - easing: easing, - complete: complete, - step: function( now, fx ) { - fx.now = Math.round( now ); - if ( fx.prop !== "height" ) { - adjust += fx.now; - } else if ( that.options.heightStyle !== "content" ) { - fx.now = Math.round( total - toHide.outerHeight() - adjust ); - adjust = 0; - } - } - }); - }, - - _toggleComplete: function( data ) { - var toHide = data.oldPanel; - - toHide - .removeClass( "ui-accordion-content-active" ) - .prev() - .removeClass( "ui-corner-top" ) - .addClass( "ui-corner-all" ); - - // Work around for rendering bug in IE (#5421) - if ( toHide.length ) { - toHide.parent()[0].className = toHide.parent()[0].className; - } - - this._trigger( "activate", null, data ); - } -}); - -})( jQuery ); - -(function( $, undefined ) { - -// used to prevent race conditions with remote data sources -var requestIndex = 0; - -$.widget( "ui.autocomplete", { - version: "1.10.2", - defaultElement: "", - options: { - appendTo: null, - autoFocus: false, - delay: 300, - minLength: 1, - position: { - my: "left top", - at: "left bottom", - collision: "none" - }, - source: null, - - // callbacks - change: null, - close: null, - focus: null, - open: null, - response: null, - search: null, - select: null - }, - - pending: 0, - - _create: function() { - // Some browsers only repeat keydown events, not keypress events, - // so we use the suppressKeyPress flag to determine if we've already - // handled the keydown event. #7269 - // Unfortunately the code for & in keypress is the same as the up arrow, - // so we use the suppressKeyPressRepeat flag to avoid handling keypress - // events when we know the keydown event was used to modify the - // search term. #7799 - var suppressKeyPress, suppressKeyPressRepeat, suppressInput, - nodeName = this.element[0].nodeName.toLowerCase(), - isTextarea = nodeName === "textarea", - isInput = nodeName === "input"; - - this.isMultiLine = - // Textareas are always multi-line - isTextarea ? true : - // Inputs are always single-line, even if inside a contentEditable element - // IE also treats inputs as contentEditable - isInput ? false : - // All other element types are determined by whether or not they're contentEditable - this.element.prop( "isContentEditable" ); - - this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; - this.isNewMenu = true; - - this.element - .addClass( "ui-autocomplete-input" ) - .attr( "autocomplete", "off" ); - - this._on( this.element, { - keydown: function( event ) { - /*jshint maxcomplexity:15*/ - if ( this.element.prop( "readOnly" ) ) { - suppressKeyPress = true; - suppressInput = true; - suppressKeyPressRepeat = true; - return; - } - - suppressKeyPress = false; - suppressInput = false; - suppressKeyPressRepeat = false; - var keyCode = $.ui.keyCode; - switch( event.keyCode ) { - case keyCode.PAGE_UP: - suppressKeyPress = true; - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - suppressKeyPress = true; - this._move( "nextPage", event ); - break; - case keyCode.UP: - suppressKeyPress = true; - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - suppressKeyPress = true; - this._keyEvent( "next", event ); - break; - case keyCode.ENTER: - case keyCode.NUMPAD_ENTER: - // when menu is open and has focus - if ( this.menu.active ) { - // #6055 - Opera still allows the keypress to occur - // which causes forms to submit - suppressKeyPress = true; - event.preventDefault(); - this.menu.select( event ); - } - break; - case keyCode.TAB: - if ( this.menu.active ) { - this.menu.select( event ); - } - break; - case keyCode.ESCAPE: - if ( this.menu.element.is( ":visible" ) ) { - this._value( this.term ); - this.close( event ); - // Different browsers have different default behavior for escape - // Single press can mean undo or clear - // Double press in IE means clear the whole form - event.preventDefault(); - } - break; - default: - suppressKeyPressRepeat = true; - // search timeout should be triggered before the input value is changed - this._searchTimeout( event ); - break; - } - }, - keypress: function( event ) { - if ( suppressKeyPress ) { - suppressKeyPress = false; - event.preventDefault(); - return; - } - if ( suppressKeyPressRepeat ) { - return; - } - - // replicate some key handlers to allow them to repeat in Firefox and Opera - var keyCode = $.ui.keyCode; - switch( event.keyCode ) { - case keyCode.PAGE_UP: - this._move( "previousPage", event ); - break; - case keyCode.PAGE_DOWN: - this._move( "nextPage", event ); - break; - case keyCode.UP: - this._keyEvent( "previous", event ); - break; - case keyCode.DOWN: - this._keyEvent( "next", event ); - break; - } - }, - input: function( event ) { - if ( suppressInput ) { - suppressInput = false; - event.preventDefault(); - return; - } - this._searchTimeout( event ); - }, - focus: function() { - this.selectedItem = null; - this.previous = this._value(); - }, - blur: function( event ) { - if ( this.cancelBlur ) { - delete this.cancelBlur; - return; - } - - clearTimeout( this.searching ); - this.close( event ); - this._change( event ); - } - }); - - this._initSource(); - this.menu = $( "