diff --git a/src/main/resources/static/app-client/main.js b/src/main/resources/static/app-client/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..f01558df5ceb24575821705bd49eae326dc9d298
--- /dev/null
+++ b/src/main/resources/static/app-client/main.js
@@ -0,0 +1,66 @@
+require([
+ 'dijit/form/Button'
+ , 'app-client/task/TasksSelect'
+ , 'dijit/form/Select'
+ , 'dojo/dom'
+ , 'app-client/socket/Socket'
+ , 'app-client/tasks'
+ , 'dojo/domReady!'
+], function(
+ Button
+ , TasksSelect
+ , Select
+ , dom
+ , Socket
+ , Tasks
+){
+ var socket = new Socket({
+ url: '/db2es/socket'
+ });
+
+ var connectBtn = new Button({
+ label: 'Connect',
+ onClick: function(){
+ socket.connect();
+ }
+ }, 'connectBtn');
+
+
+
+ var sendBtn = new TasksSelect({
+ socket: socket
+ }, 'sendBtn');
+
+/* sendBtn.on('subscribed', function() {
+ console.log('Subscrito');
+ setTimeout(function() {
+ var listTask = ["citation-job", "animaltracking-job"];
+ socket.sendByTopic('/topic' + sendBtn.reqChannel, listTask);
+ }, 500);
+ });*/
+
+
+ socket.on('connected', function(url) {
+ console.log('Conectado');
+
+
+
+/* socket.subscribe('/topic/greeting', function(message) {
+ console.log('Recibo gretting: ' + message);
+ });
+
+ socket.subscribe('/topic/jobs/list', function(message) {
+ console.log('Recibo jobs: ' + message);
+ });*/
+ var tasks = new Tasks({
+ socket: socket
+ }, 'tasks');
+
+ /*socket.subscribe('/topic/jobs/list', function(message) {
+ console.log('Recibo jobs: ' + message);
+ });*/
+
+
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/resources/static/app-client/socket/Socket.js b/src/main/resources/static/app-client/socket/Socket.js
new file mode 100644
index 0000000000000000000000000000000000000000..35b50a8eee1c30bd8de3039a1b0e3f57f886fce6
--- /dev/null
+++ b/src/main/resources/static/app-client/socket/Socket.js
@@ -0,0 +1,68 @@
+define([
+ 'dojo/_base/declare'
+ , 'dojo/_base/lang'
+ , 'dojo/Evented'
+ , 'sockjs'
+ , 'stomp'
+], function(
+ declare
+ , lang
+ , Evented
+ , SockJS
+ , Stomp
+){
+ return declare([Evented], {
+ _ws: null,
+ _client: null,
+ url: null,
+
+ constructor: function(args) {
+ this._subscribes = {};
+ this._isConnected = false;
+
+ lang.mixin(this, args);
+
+ this.on('connect', this._connect);
+ this.on('error', this._error);
+ },
+
+ connect: function() {
+ this.emit(this.url ? 'connect' : 'error', this.url);
+ },
+
+ isConnected: function() {
+ return this._isConnected;
+ },
+
+ subscribe: function(channel, callback) {
+ this._subscribes[channel] = callback;
+
+ this._client.subscribe(channel, function(message) {
+ console.debug('SUBSCRIBE', channel, message);
+ message.body = JSON.parse(message.body);
+ callback(message);
+ });
+ },
+
+ send: function(channel, message) {
+ console.debug('SEND', channel, message);
+ this._client.send(channel, {}, JSON.stringify(message));
+ },
+
+ _connect: function() {
+
+ this._ws = new SockJS(this.url);
+ this._client = window.Stomp.over(this._ws);
+ this._client.connect({}, lang.hitch(this, this._connected));
+ },
+
+ _connected: function() {
+ this._isConnected = true;
+ this.emit('connected', this);
+ },
+
+ _error: function(url) {
+ console.error('ERROR');
+ }
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/static/app-client/socket/SocketMock.js b/src/main/resources/static/app-client/socket/SocketMock.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3d2da9fd9de192dfbf45b6019e6bf59cd28eba3
--- /dev/null
+++ b/src/main/resources/static/app-client/socket/SocketMock.js
@@ -0,0 +1,30 @@
+define([
+ 'dojo/_base/declare'
+ , 'dojo/_base/lang'
+ , 'app-client/socket/Socket'
+], function(
+ declare
+ , lang
+ , Socket
+){
+ return declare([Socket], {
+
+ subscribe: function(channel, callback) {
+ this._subscribes[channel] = callback;
+ },
+
+ sendByTopic: function(channel, message) {
+
+ this._subscribes[channel] && this._subscribes[channel](message);
+ },
+
+ send: function(channel, message) {
+ console.log('Send', channel, message);
+ },
+
+ _connect: function() {
+ console.log('Connect');
+ setTimeout(lang.hitch(this, this._connected), 500);
+ }
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/static/app-client/task/TasksSelect.js b/src/main/resources/static/app-client/task/TasksSelect.js
new file mode 100644
index 0000000000000000000000000000000000000000..edf2dbfd8222a2debce4cad428094d05b57c9c8c
--- /dev/null
+++ b/src/main/resources/static/app-client/task/TasksSelect.js
@@ -0,0 +1,144 @@
+define([
+ 'dojo/_base/declare'
+ , 'dojo/_base/lang'
+ , 'dojo/on'
+ , 'dojo/Evented'
+ , 'dojo/dom-construct'
+ , 'dijit/_WidgetBase'
+ , 'dijit/form/Select'
+ , 'dijit/form/Button'
+], function(
+ declare
+ , lang
+ , on
+ , Evented
+ , domConstruct
+ , _Widget
+ , Select
+ , Button
+){
+ return declare([_Widget, Evented], {
+ socket: null,
+ reqChannel: '/jobs/list',
+ url: null,
+ jobsList: null,
+
+ constructor: function(args) {
+ this._subscriptions = {};
+
+ this.i18n = {
+ "indexing-animalTracking-job": "Tracking de animales",
+ "indexing-platformTracking-job": "Tracking de plataformas",
+ "indexing-citation-job": "Citas corológicas",
+ "indexing-timeseriesSurvey-job": "Estaciones fijas con series temporales",
+ "indexing-timeSeries-job": "Series temporales",
+ "indexing-attributeSeries-job": "Series de atributo de Infrastructuras",
+ "indexing-isolines-job": "Isolíneas",
+ "indexing-area-job": "Áreas",
+ "indexing-objectCollectingSurvey-job": "Indexar object survey",
+ "indexing-objectCollectingseries-job": "Serie de colección de objetos",
+ "indexing-infrastructure-job": "Infrastructuras",
+ "indexing-administrativeDomains-job": "Dominios de administración",
+ "indexing-document-job": "Indexar documentos",
+ "indexing-classificationsDomains-job": "Dominios de clasificación",
+ "indexing-parametersDomains-job": "Dominios de parámetros",
+ "indexing-qualityDomains-job": "Dominios de calidad de los datos",
+ "indexing-qualifyDomains-job": "Dominios de calificación de objetos",
+ "indexing-taxonomyDomains-job": "Dominios de taxonomía",
+ "indexing-ancillaryDataDomains-job": "Dominios de datos auxiliares",
+ "indexing-toponymias-job": "Toponimias",
+ "indexing-taxonomy-job": "Taxonomía",
+ "indexing-administrative-job": "Administración",
+ "indexing-atlas-job": "Atlas"
+ };
+
+ lang.mixin(this, args);
+ },
+
+ postCreate: function() {
+ this._createLayout();
+ this._startSocket();
+ },
+
+ _createLayout: function() {
+ this._selectNode = domConstruct.create('span', null, this.domNode);
+ this._btnNode = domConstruct.create('span', null, this.domNode);
+ },
+
+ _startSocket: function(value) {
+ if (this.socket) {
+ this.socket.isConnected() && this._onSocketOpened();
+ this.socket.on('connected', lang.hitch(this, this._onSocketOpened));
+ }
+ },
+
+ _onSocketOpened: function() {
+ this.emit('socket-opened');
+ this._onSubscriptions();
+ },
+
+ _onSubscriptions: function() {
+
+ this.socket.subscribe('/topic' + this.reqChannel, lang.hitch(this, this._onReceivedJobsList));
+ this.emit('subscribed');
+ this.socket.send('/app' + this.reqChannel, {});
+ },
+
+ _onReceivedJobsList: function(message) {
+
+ this.jobsList = this._parseJobsList(message.body);
+ this.emit('received-jobs-list', this.jobsList);
+ this._createSelectJob();
+ this._createBtnRunJob();
+ },
+
+ _parseJobsList: function(jobsIn) {
+ var jobs = [];
+ for (var i = 0; i < jobsIn.length; i++) {
+ jobs.push({
+ value: jobsIn[i],
+ label: this.i18n[jobsIn[i]]
+ });
+ }
+
+ return jobs;
+ },
+
+ _createSelectJob: function() {
+ this._selectWidget = new Select({
+ name: 'selectJob',
+ options: this.jobsList,
+ style: 'width: 90%'
+ }, this._selectNode);
+ },
+
+ _createBtnRunJob: function() {
+ this._btnWidget = new Button({
+ onClick: lang.hitch(this, this._runJob),
+ iconClass: 'dijitIconTask',
+ label: null
+ }, this._btnNode);
+ },
+
+ _runJob: function() {
+ var jobId = this._selectWidget.get('value');
+
+ this.socket.send('/app/jobs/indexing', {
+ jobName: jobId
+ });
+ },
+
+ _sendRequest: function() {
+ this.socket.send(this.reqChannel, {});
+ },
+
+ _createJobList: function() {
+
+ },
+
+ _error: function(url) {
+ console.error('ERROR');
+ }
+
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/static/app-client/taskitem.js b/src/main/resources/static/app-client/taskitem.js
new file mode 100644
index 0000000000000000000000000000000000000000..92ef7cdd7dcc1f1df75cb8b9c5112bafbcdc9ea4
--- /dev/null
+++ b/src/main/resources/static/app-client/taskitem.js
@@ -0,0 +1,79 @@
+define([
+ 'dojo/_base/declare'
+ , 'dojo/_base/lang'
+ , 'dojo/Evented'
+ , 'dojo/dom-construct'
+ , 'dijit/_Widget'
+ , 'dijit/form/Button'
+], function(
+ declare
+ , lang
+ , Evented
+ , domConstruct
+ , _WidgetBase
+ , Button
+){
+ return declare([_WidgetBase, Evented], {
+
+
+ 'class': 'taskContainer',
+
+ constructor: function(args) {
+ lang.mixin(this, args);
+ },
+
+ postCreate: function() {
+ this.containerNode = domConstruct.create('div', null, this.domNode, 'last');
+ this.task && this.update(this.task);
+ //this.on('task-status', lang.hitch(this, this._createButton));
+ this._createButton();
+ },
+
+
+ add: function(task) {
+
+ for (var key in task) {
+ domConstruct.create('div', {
+ innerHTML: key + ' - ' + task[key],
+ 'class': 'taskItem'
+ }, this.containerNode, 'last');
+ }
+ },
+
+ update: function(task) {
+ domConstruct.empty(this.containerNode);
+ this.add(task);
+ },
+
+ _createButton: function(task) {
+ /*switch(task.status) {
+ case 'STOPPED':
+ console.log('Create button restart');
+ break;
+ case 'STARTING':
+ console.log('Create button stop');
+ break;
+ case 'STOPPING':
+ console.log('Create button delete');
+ break;
+ }*/
+ this._btnStop = new Button({
+ label: 'Parar',
+ onClick: lang.hitch(this, this._stopTask)
+ }).placeAt(this.domNode, 'last');
+
+ new Button({
+ label: 'Eliminar',
+ onClick: lang.hitch(this, this._deleteTask)
+ }).placeAt(this.domNode, 'last');
+ },
+
+ _stopTask: function() {
+ this.emit('stop-task', this.task);
+ },
+
+ _deleteTask: function() {
+ this.emit('delete-task', this.task);
+ }
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/static/app-client/tasks.js b/src/main/resources/static/app-client/tasks.js
new file mode 100644
index 0000000000000000000000000000000000000000..f0204559e2c27f5504b1520119e0b9d893ab44f1
--- /dev/null
+++ b/src/main/resources/static/app-client/tasks.js
@@ -0,0 +1,79 @@
+define([
+ 'dojo/_base/declare'
+ , 'dojo/_base/lang'
+ , 'dojo/Evented'
+ , 'dijit/_Widget'
+ , 'app-client/taskitem'
+], function(
+ declare
+ , lang
+ , Evented
+ , _Widget
+ , TaskItem
+){
+ return declare([_Widget, Evented], {
+
+ socket: null,
+ _children: null,
+
+ constructor: function(args) {
+ lang.mixin(this, args);
+
+ this._subscribes = {};
+ this._children = {};
+
+ this.socket.subscribe('/topic/jobs/status', lang.hitch(this, this._refresh));
+
+ this.on("new-task", lang.hitch(this, this._newTask));
+ this.on("update-task", lang.hitch(this, this._updateTask));
+ },
+
+ _refresh: function(msg) {
+ var message = msg.body;
+
+ if (Array.isArray(message)) {
+ for (var i = 0; i < message.length; i++) {
+ var task = message[i];
+ this.emit(this._isNew(task) ? "new-task" : "update-task", task);
+ }
+ }
+ },
+
+ _newTask: function(task) {
+ this._children[task.taskId] = taskItem = new TaskItem({
+ task: task
+ }).placeAt(this.domNode, 'first');
+
+ taskItem.on('stop-task', lang.hitch(this, this._stopTask));
+ taskItem.on('delete-task', lang.hitch(this, this._deleteTask));
+ },
+
+ _updateTask: function(task) {
+ var taskWidget = this._getChildren(task.taskId);
+ taskWidget && taskWidget.update(task);
+ },
+
+ _stopTask: function(task) {
+ this.socket.send('/app/jobs/stop', {
+ jobId: task.taskId,
+ jobName: task.taskName
+ });
+ },
+
+ _deleteTask: function(task) {
+ this.socket.send('/app/jobs/delete', {
+ jobId: task.taskId,
+ jobName: task.taskName
+ });
+ },
+
+ _isNew: function(task) {
+ return !this._children[task.taskId];
+ },
+
+ _getChildren: function(id) {
+ return this._children[id];
+ }
+
+ });
+});
\ No newline at end of file
diff --git a/src/main/resources/static/dijit b/src/main/resources/static/dijit
new file mode 160000
index 0000000000000000000000000000000000000000..806bee828790ce1ab76115437731e65d5cfd7b56
--- /dev/null
+++ b/src/main/resources/static/dijit
@@ -0,0 +1 @@
+Subproject commit 806bee828790ce1ab76115437731e65d5cfd7b56
diff --git a/src/main/resources/static/dojo/AdapterRegistry.js b/src/main/resources/static/dojo/AdapterRegistry.js
new file mode 100644
index 0000000000000000000000000000000000000000..cc70163bfd204fc54c632ce5e568242cf589b90d
--- /dev/null
+++ b/src/main/resources/static/dojo/AdapterRegistry.js
@@ -0,0 +1,106 @@
+define(["./_base/kernel", "./_base/lang"], function(dojo, lang){
+// module:
+// dojo/AdapterRegistry
+
+var AdapterRegistry = dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
+ // summary:
+ // A registry to make contextual calling/searching easier.
+ // description:
+ // Objects of this class keep list of arrays in the form [name, check,
+ // wrap, directReturn] that are used to determine what the contextual
+ // result of a set of checked arguments is. All check/wrap functions
+ // in this registry should be of the same arity.
+ // example:
+ // | // create a new registry
+ // | var reg = new dojo.AdapterRegistry();
+ // | reg.register("handleString",
+ // | dojo.isString,
+ // | function(str){
+ // | // do something with the string here
+ // | }
+ // | );
+ // | reg.register("handleArr",
+ // | dojo.isArray,
+ // | function(arr){
+ // | // do something with the array here
+ // | }
+ // | );
+ // |
+ // | // now we can pass reg.match() *either* an array or a string and
+ // | // the value we pass will get handled by the right function
+ // | reg.match("someValue"); // will call the first function
+ // | reg.match(["someValue"]); // will call the second
+
+ this.pairs = [];
+ this.returnWrappers = returnWrappers || false; // Boolean
+};
+
+lang.extend(AdapterRegistry, {
+ register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
+ // summary:
+ // register a check function to determine if the wrap function or
+ // object gets selected
+ // name:
+ // a way to identify this matcher.
+ // check:
+ // a function that arguments are passed to from the adapter's
+ // match() function. The check function should return true if the
+ // given arguments are appropriate for the wrap function.
+ // directReturn:
+ // If directReturn is true, the value passed in for wrap will be
+ // returned instead of being called. Alternately, the
+ // AdapterRegistry can be set globally to "return not call" using
+ // the returnWrappers property. Either way, this behavior allows
+ // the registry to act as a "search" function instead of a
+ // function interception library.
+ // override:
+ // If override is given and true, the check function will be given
+ // highest priority. Otherwise, it will be the lowest priority
+ // adapter.
+ this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
+ },
+
+ match: function(/* ... */){
+ // summary:
+ // Find an adapter for the given arguments. If no suitable adapter
+ // is found, throws an exception. match() accepts any number of
+ // arguments, all of which are passed to all matching functions
+ // from the registered pairs.
+ for(var i = 0; i < this.pairs.length; i++){
+ var pair = this.pairs[i];
+ if(pair[1].apply(this, arguments)){
+ if((pair[3])||(this.returnWrappers)){
+ return pair[2];
+ }else{
+ return pair[2].apply(this, arguments);
+ }
+ }
+ }
+ throw new Error("No match found");
+ },
+
+ unregister: function(name){
+ // summary:
+ // Remove a named adapter from the registry
+ // name: String
+ // The name of the adapter.
+ // returns: Boolean
+ // Returns true if operation is successful.
+ // Returns false if operation fails.
+
+ // FIXME: this is kind of a dumb way to handle this. On a large
+ // registry this will be slow-ish and we can use the name as a lookup
+ // should we choose to trade memory for speed.
+ for(var i = 0; i < this.pairs.length; i++){
+ var pair = this.pairs[i];
+ if(pair[0] == name){
+ this.pairs.splice(i, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+});
+
+return AdapterRegistry;
+});
diff --git a/src/main/resources/static/dojo/Deferred.js b/src/main/resources/static/dojo/Deferred.js
new file mode 100644
index 0000000000000000000000000000000000000000..79d61f7b13c57112019b7e702354bcb07273e2ff
--- /dev/null
+++ b/src/main/resources/static/dojo/Deferred.js
@@ -0,0 +1,320 @@
+define([
+ "./has",
+ "./_base/lang",
+ "./errors/CancelError",
+ "./promise/Promise",
+ "./has!config-deferredInstrumentation?./promise/instrumentation"
+], function(has, lang, CancelError, Promise, instrumentation){
+ "use strict";
+
+ // module:
+ // dojo/Deferred
+
+ var PROGRESS = 0,
+ RESOLVED = 1,
+ REJECTED = 2;
+ var FULFILLED_ERROR_MESSAGE = "This deferred has already been fulfilled.";
+
+ var freezeObject = Object.freeze || function(){};
+
+ var signalWaiting = function(waiting, type, result, rejection, deferred){
+ if(has("config-deferredInstrumentation")){
+ if(type === REJECTED && Deferred.instrumentRejected && waiting.length === 0){
+ Deferred.instrumentRejected(result, false, rejection, deferred);
+ }
+ }
+
+ for(var i = 0; i < waiting.length; i++){
+ signalListener(waiting[i], type, result, rejection);
+ }
+ };
+
+ var signalListener = function(listener, type, result, rejection){
+ var func = listener[type];
+ var deferred = listener.deferred;
+ if(func){
+ try{
+ var newResult = func(result);
+ if(type === PROGRESS){
+ if(typeof newResult !== "undefined"){
+ signalDeferred(deferred, type, newResult);
+ }
+ }else{
+ if(newResult && typeof newResult.then === "function"){
+ listener.cancel = newResult.cancel;
+ newResult.then(
+ // Only make resolvers if they're actually going to be used
+ makeDeferredSignaler(deferred, RESOLVED),
+ makeDeferredSignaler(deferred, REJECTED),
+ makeDeferredSignaler(deferred, PROGRESS));
+ return;
+ }
+ signalDeferred(deferred, RESOLVED, newResult);
+ }
+ }catch(error){
+ signalDeferred(deferred, REJECTED, error);
+ }
+ }else{
+ signalDeferred(deferred, type, result);
+ }
+
+ if(has("config-deferredInstrumentation")){
+ if(type === REJECTED && Deferred.instrumentRejected){
+ Deferred.instrumentRejected(result, !!func, rejection, deferred.promise);
+ }
+ }
+ };
+
+ var makeDeferredSignaler = function(deferred, type){
+ return function(value){
+ signalDeferred(deferred, type, value);
+ };
+ };
+
+ var signalDeferred = function(deferred, type, result){
+ if(!deferred.isCanceled()){
+ switch(type){
+ case PROGRESS:
+ deferred.progress(result);
+ break;
+ case RESOLVED:
+ deferred.resolve(result);
+ break;
+ case REJECTED:
+ deferred.reject(result);
+ break;
+ }
+ }
+ };
+
+ var Deferred = function(canceler){
+ // summary:
+ // Creates a new deferred. This API is preferred over
+ // `dojo/_base/Deferred`.
+ // description:
+ // Creates a new deferred, as an abstraction over (primarily)
+ // asynchronous operations. The deferred is the private interface
+ // that should not be returned to calling code. That's what the
+ // `promise` is for. See `dojo/promise/Promise`.
+ // canceler: Function?
+ // Will be invoked if the deferred is canceled. The canceler
+ // receives the reason the deferred was canceled as its argument.
+ // The deferred is rejected with its return value, or a new
+ // `dojo/errors/CancelError` instance.
+
+ // promise: dojo/promise/Promise
+ // The public promise object that clients can add callbacks to.
+ var promise = this.promise = new Promise();
+
+ var deferred = this;
+ var fulfilled, result, rejection;
+ var canceled = false;
+ var waiting = [];
+
+ if(has("config-deferredInstrumentation") && Error.captureStackTrace){
+ Error.captureStackTrace(deferred, Deferred);
+ Error.captureStackTrace(promise, Deferred);
+ }
+
+ this.isResolved = promise.isResolved = function(){
+ // summary:
+ // Checks whether the deferred has been resolved.
+ // returns: Boolean
+
+ return fulfilled === RESOLVED;
+ };
+
+ this.isRejected = promise.isRejected = function(){
+ // summary:
+ // Checks whether the deferred has been rejected.
+ // returns: Boolean
+
+ return fulfilled === REJECTED;
+ };
+
+ this.isFulfilled = promise.isFulfilled = function(){
+ // summary:
+ // Checks whether the deferred has been resolved or rejected.
+ // returns: Boolean
+
+ return !!fulfilled;
+ };
+
+ this.isCanceled = promise.isCanceled = function(){
+ // summary:
+ // Checks whether the deferred has been canceled.
+ // returns: Boolean
+
+ return canceled;
+ };
+
+ this.progress = function(update, strict){
+ // summary:
+ // Emit a progress update on the deferred.
+ // description:
+ // Emit a progress update on the deferred. Progress updates
+ // can be used to communicate updates about the asynchronous
+ // operation before it has finished.
+ // update: any
+ // The progress update. Passed to progbacks.
+ // strict: Boolean?
+ // If strict, will throw an error if the deferred has already
+ // been fulfilled and consequently no progress can be emitted.
+ // returns: dojo/promise/Promise
+ // Returns the original promise for the deferred.
+
+ if(!fulfilled){
+ signalWaiting(waiting, PROGRESS, update, null, deferred);
+ return promise;
+ }else if(strict === true){
+ throw new Error(FULFILLED_ERROR_MESSAGE);
+ }else{
+ return promise;
+ }
+ };
+
+ this.resolve = function(value, strict){
+ // summary:
+ // Resolve the deferred.
+ // description:
+ // Resolve the deferred, putting it in a success state.
+ // value: any
+ // The result of the deferred. Passed to callbacks.
+ // strict: Boolean?
+ // If strict, will throw an error if the deferred has already
+ // been fulfilled and consequently cannot be resolved.
+ // returns: dojo/promise/Promise
+ // Returns the original promise for the deferred.
+
+ if(!fulfilled){
+ // Set fulfilled, store value. After signaling waiting listeners unset
+ // waiting.
+ signalWaiting(waiting, fulfilled = RESOLVED, result = value, null, deferred);
+ waiting = null;
+ return promise;
+ }else if(strict === true){
+ throw new Error(FULFILLED_ERROR_MESSAGE);
+ }else{
+ return promise;
+ }
+ };
+
+ var reject = this.reject = function(error, strict){
+ // summary:
+ // Reject the deferred.
+ // description:
+ // Reject the deferred, putting it in an error state.
+ // error: any
+ // The error result of the deferred. Passed to errbacks.
+ // strict: Boolean?
+ // If strict, will throw an error if the deferred has already
+ // been fulfilled and consequently cannot be rejected.
+ // returns: dojo/promise/Promise
+ // Returns the original promise for the deferred.
+
+ if(!fulfilled){
+ if(has("config-deferredInstrumentation") && Error.captureStackTrace){
+ Error.captureStackTrace(rejection = {}, reject);
+ }
+ signalWaiting(waiting, fulfilled = REJECTED, result = error, rejection, deferred);
+ waiting = null;
+ return promise;
+ }else if(strict === true){
+ throw new Error(FULFILLED_ERROR_MESSAGE);
+ }else{
+ return promise;
+ }
+ };
+
+ this.then = promise.then = function(callback, errback, progback){
+ // summary:
+ // Add new callbacks to the deferred.
+ // description:
+ // Add new callbacks to the deferred. Callbacks can be added
+ // before or after the deferred is fulfilled.
+ // callback: Function?
+ // Callback to be invoked when the promise is resolved.
+ // Receives the resolution value.
+ // errback: Function?
+ // Callback to be invoked when the promise is rejected.
+ // Receives the rejection error.
+ // progback: Function?
+ // Callback to be invoked when the promise emits a progress
+ // update. Receives the progress update.
+ // returns: dojo/promise/Promise
+ // Returns a new promise for the result of the callback(s).
+ // This can be used for chaining many asynchronous operations.
+
+ var listener = [progback, callback, errback];
+ // Ensure we cancel the promise we're waiting for, or if callback/errback
+ // have returned a promise, cancel that one.
+ listener.cancel = promise.cancel;
+ listener.deferred = new Deferred(function(reason){
+ // Check whether cancel is really available, returned promises are not
+ // required to expose `cancel`
+ return listener.cancel && listener.cancel(reason);
+ });
+ if(fulfilled && !waiting){
+ signalListener(listener, fulfilled, result, rejection);
+ }else{
+ waiting.push(listener);
+ }
+ return listener.deferred.promise;
+ };
+
+ this.cancel = promise.cancel = function(reason, strict){
+ // summary:
+ // Inform the deferred it may cancel its asynchronous operation.
+ // description:
+ // Inform the deferred it may cancel its asynchronous operation.
+ // The deferred's (optional) canceler is invoked and the
+ // deferred will be left in a rejected state. Can affect other
+ // promises that originate with the same deferred.
+ // reason: any
+ // A message that may be sent to the deferred's canceler,
+ // explaining why it's being canceled.
+ // strict: Boolean?
+ // If strict, will throw an error if the deferred has already
+ // been fulfilled and consequently cannot be canceled.
+ // returns: any
+ // Returns the rejection reason if the deferred was canceled
+ // normally.
+
+ if(!fulfilled){
+ // Cancel can be called even after the deferred is fulfilled
+ if(canceler){
+ var returnedReason = canceler(reason);
+ reason = typeof returnedReason === "undefined" ? reason : returnedReason;
+ }
+ canceled = true;
+ if(!fulfilled){
+ // Allow canceler to provide its own reason, but fall back to a CancelError
+ if(typeof reason === "undefined"){
+ reason = new CancelError();
+ }
+ reject(reason);
+ return reason;
+ }else if(fulfilled === REJECTED && result === reason){
+ return reason;
+ }
+ }else if(strict === true){
+ throw new Error(FULFILLED_ERROR_MESSAGE);
+ }
+ };
+
+ freezeObject(promise);
+ };
+
+ Deferred.prototype.toString = function(){
+ // returns: String
+ // Returns `[object Deferred]`.
+
+ return "[object Deferred]";
+ };
+
+ if(instrumentation){
+ instrumentation(Deferred);
+ }
+
+ return Deferred;
+});
diff --git a/src/main/resources/static/dojo/DeferredList.js b/src/main/resources/static/dojo/DeferredList.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c3e95067ecb72122326a51e8b2347cd1a8f5745
--- /dev/null
+++ b/src/main/resources/static/dojo/DeferredList.js
@@ -0,0 +1,84 @@
+define(["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
+ // module:
+ // dojo/DeferredList
+
+
+dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
+ // summary:
+ // Deprecated, use dojo/promise/all instead.
+ // Provides event handling for a group of Deferred objects.
+ // description:
+ // DeferredList takes an array of existing deferreds and returns a new deferred of its own
+ // this new deferred will typically have its callback fired when all of the deferreds in
+ // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
+ // fireOnOneErrback, will fire before all the deferreds as appropriate
+ // list:
+ // The list of deferreds to be synchronizied with this DeferredList
+ // fireOnOneCallback:
+ // Will cause the DeferredLists callback to be fired as soon as any
+ // of the deferreds in its list have been fired instead of waiting until
+ // the entire list has finished
+ // fireonOneErrback:
+ // Will cause the errback to fire upon any of the deferreds errback
+ // canceller:
+ // A deferred canceller function, see dojo.Deferred
+ var resultList = [];
+ Deferred.call(this);
+ var self = this;
+ if(list.length === 0 && !fireOnOneCallback){
+ this.resolve([0, []]);
+ }
+ var finished = 0;
+ darray.forEach(list, function(item, i){
+ item.then(function(result){
+ if(fireOnOneCallback){
+ self.resolve([i, result]);
+ }else{
+ addResult(true, result);
+ }
+ },function(error){
+ if(fireOnOneErrback){
+ self.reject(error);
+ }else{
+ addResult(false, error);
+ }
+ if(consumeErrors){
+ return null;
+ }
+ throw error;
+ });
+ function addResult(succeeded, result){
+ resultList[i] = [succeeded, result];
+ finished++;
+ if(finished === list.length){
+ self.resolve(resultList);
+ }
+
+ }
+ });
+};
+dojo.DeferredList.prototype = new Deferred();
+
+dojo.DeferredList.prototype.gatherResults = function(deferredList){
+ // summary:
+ // Gathers the results of the deferreds for packaging
+ // as the parameters to the Deferred Lists' callback
+ // deferredList: dojo/DeferredList
+ // The deferred list from which this function gathers results.
+ // returns: dojo/DeferredList
+ // The newly created deferred list which packs results as
+ // parameters to its callback.
+
+ var d = new dojo.DeferredList(deferredList, false, true, false);
+ d.addCallback(function(results){
+ var ret = [];
+ darray.forEach(results, function(result){
+ ret.push(result[1]);
+ });
+ return ret;
+ });
+ return d;
+};
+
+return dojo.DeferredList;
+});
diff --git a/src/main/resources/static/dojo/Evented.js b/src/main/resources/static/dojo/Evented.js
new file mode 100644
index 0000000000000000000000000000000000000000..661b6d27ced02d7473304d2d9d94e7bd71fad8ef
--- /dev/null
+++ b/src/main/resources/static/dojo/Evented.js
@@ -0,0 +1,35 @@
+define(["./aspect", "./on"], function(aspect, on){
+ // module:
+ // dojo/Evented
+
+ "use strict";
+ var after = aspect.after;
+ function Evented(){
+ // summary:
+ // A class that can be used as a mixin or base class,
+ // to add on() and emit() methods to a class
+ // for listening for events and emitting events:
+ //
+ // | define(["dojo/Evented"], function(Evented){
+ // | var EventedWidget = dojo.declare([Evented, dijit._Widget], {...});
+ // | widget = new EventedWidget();
+ // | widget.on("open", function(event){
+ // | ... do something with event
+ // | });
+ // |
+ // | widget.emit("open", {name:"some event", ...});
+ }
+ Evented.prototype = {
+ on: function(type, listener){
+ return on.parse(this, type, listener, function(target, type){
+ return after(target, 'on' + type, listener, true);
+ });
+ },
+ emit: function(type, event){
+ var args = [this];
+ args.push.apply(args, arguments);
+ return on.emit.apply(on, args);
+ }
+ };
+ return Evented;
+});
diff --git a/src/main/resources/static/dojo/LICENSE b/src/main/resources/static/dojo/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..b1ddd3408a21ca9aa315ddc987b8f868f67b6f87
--- /dev/null
+++ b/src/main/resources/static/dojo/LICENSE
@@ -0,0 +1,195 @@
+Dojo is available under *either* the terms of the modified BSD license *or* the
+Academic Free License version 2.1. As a recipient of Dojo, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of the Dojo Foundation. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the AFL or BSD licenses that Dojo is distributed under.
+
+The text of the AFL and BSD licenses is reproduced below.
+
+-------------------------------------------------------------------------------
+The "New" BSD License:
+**********************
+
+Copyright (c) 2005-2013, The Dojo Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Dojo Foundation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+The Academic Free License, v. 2.1:
+**********************************
+
+This Academic Free License (the "License") applies to any original work of
+authorship (the "Original Work") whose owner (the "Licensor") has placed the
+following notice immediately following the copyright notice for the Original
+Work:
+
+Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license to do the
+following:
+
+a) to reproduce the Original Work in copies;
+
+b) to prepare derivative works ("Derivative Works") based upon the Original
+Work;
+
+c) to distribute copies of the Original Work and Derivative Works to the
+public;
+
+d) to perform the Original Work publicly; and
+
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under patent
+claims owned or controlled by the Licensor that are embodied in the Original
+Work as furnished by the Licensor, to make, use, sell and offer for sale the
+Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred
+form of the Original Work for making modifications to it and all available
+documentation describing how to modify the Original Work. Licensor hereby
+agrees to provide a machine-readable copy of the Source Code of the Original
+Work along with each copy of the Original Work that Licensor distributes.
+Licensor reserves the right to satisfy this obligation by placing a
+machine-readable copy of the Source Code in an information repository
+reasonably calculated to permit inexpensive and convenient access by You for as
+long as Licensor continues to distribute the Original Work, and by publishing
+the address of that information repository in a notice immediately following
+the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names
+of any contributors to the Original Work, nor any of their trademarks or
+service marks, may be used to endorse or promote products derived from this
+Original Work without express prior written permission of the Licensor. Nothing
+in this License shall be deemed to grant any rights to trademarks, copyrights,
+patents, trade secrets or any other intellectual property of Licensor except as
+expressly stated herein. No patent license is granted to make, use, sell or
+offer to sell embodiments of any patent claims other than the licensed claims
+defined in Section 2. No right is granted to the trademarks of Licensor even if
+such marks are included in the Original Work. Nothing in this License shall be
+interpreted to prohibit Licensor from licensing under different terms from this
+License any Original Work that Licensor otherwise would have a right to
+license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative
+Works that You create, all copyright, patent or trademark notices from the
+Source Code of the Original Work, as well as any notices of licensing and any
+descriptive text identified therein as an "Attribution Notice." You must cause
+the Source Code for any Derivative Works that You create to carry a prominent
+Attribution Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
+the copyright in and to the Original Work and the patent rights granted herein
+by Licensor are owned by the Licensor or are sublicensed to You under the terms
+of this License with the permission of the contributor(s) of those copyrights
+and patent rights. Except as expressly stated in the immediately proceeding
+sentence, the Original Work is provided under this License on an "AS IS" BASIS
+and WITHOUT WARRANTY, either express or implied, including, without limitation,
+the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU.
+This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No
+license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, shall the
+Licensor be liable to any person for any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License
+or the use of the Original Work including, without limitation, damages for loss
+of goodwill, work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses. This limitation of liability shall not
+apply to liability for death or personal injury resulting from Licensor's
+negligence to the extent applicable law prohibits such limitation. Some
+jurisdictions do not allow the exclusion or limitation of incidental or
+consequential damages, so this exclusion and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work or
+a Derivative Work, You must make a reasonable effort under the circumstances to
+obtain the express assent of recipients to the terms of this License. Nothing
+else but this License (or another written agreement between Licensor and You)
+grants You permission to create Derivative Works based upon the Original Work
+or to exercise any of the rights granted in Section 1 herein, and any attempt
+to do so except under the terms of this License (or another written agreement
+between Licensor and You) is expressly prohibited by U.S. copyright law, the
+equivalent laws of other countries, and by international treaty. Therefore, by
+exercising any of the rights granted to You in Section 1 herein, You indicate
+Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically
+and You may no longer exercise any of the rights granted to You by this License
+as of the date You commence an action, including a cross-claim or counterclaim,
+against Licensor or any licensee alleging that the Original Work infringes a
+patent. This termination provision shall not apply for an action alleging
+patent infringement by combinations of the Original Work with other software or
+hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
+License may be brought only in the courts of a jurisdiction wherein the
+Licensor resides or in which Licensor conducts its primary business, and under
+the laws of that jurisdiction excluding its conflict-of-law provisions. The
+application of the United Nations Convention on Contracts for the International
+Sale of Goods is expressly excluded. Any use of the Original Work outside the
+scope of this License or after its termination shall be subject to the
+requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et
+seq., the equivalent laws of other countries, and international treaty. This
+section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or
+seeking damages relating thereto, the prevailing party shall be entitled to
+recover its costs and expenses, including, without limitation, reasonable
+attorneys' fees and costs incurred in connection with such action, including
+any appeal of such action. This section shall survive the termination of this
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning
+the subject matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent necessary to
+make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether
+in upper or lower case, means an individual or a legal entity exercising rights
+under, and complying with all of the terms of, this License. For legal
+entities, "You" includes any entity that controls, is controlled by, or is
+under common control with you. For purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management of such
+entity, whether by contract or otherwise, or (ii) ownership of fifty percent
+(50%) or more of the outstanding shares, or (iii) beneficial ownership of such
+entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise
+restricted or conditioned by this License or by law, and Licensor promises not
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved.
+Permission is hereby granted to copy and distribute this license without
+modification. This license may not be modified without the express written
+permission of its copyright owner.
diff --git a/src/main/resources/static/dojo/NodeList-data.js b/src/main/resources/static/dojo/NodeList-data.js
new file mode 100644
index 0000000000000000000000000000000000000000..c731a35c5c64dd96b4b2611cf86de0d66b111ad4
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-data.js
@@ -0,0 +1,198 @@
+define([
+ "./_base/kernel", "./query", "./_base/lang", "./_base/array", "./dom-attr"
+], function(dojo, query, lang, array, attr){
+
+ // module:
+ // dojo/NodeList-data
+
+ /*=====
+ return function(){
+ // summary:
+ // Adds data() and removeData() methods to NodeList, and returns NodeList constructor.
+ };
+ =====*/
+
+ var NodeList = query.NodeList;
+
+ var dataCache = {}, x = 0, dataattr = "data-dojo-dataid",
+ dopid = function(node){
+ // summary:
+ // Return a uniqueish ID for the passed node reference
+ var pid = attr.get(node, dataattr);
+ if(!pid){
+ pid = "pid" + (x++);
+ attr.set(node, dataattr, pid);
+ }
+ return pid;
+ }
+ ;
+
+ //>>excludeStart("debugging", true);
+ // An alias to the private dataCache for NodeList-data. NEVER USE THIS!
+ // This private is only exposed for the benefit of unit testing, and is
+ // removed during the build process.
+ NodeList._nodeDataCache = dojo._nodeDataCache = dataCache;
+ //>>excludeEnd("debugging");
+
+ var dodata = dojo._nodeData = function(node, key, value){
+ // summary:
+ // Private helper for dojo/NodeList.data for single node data access. Refer to NodeList.data
+ // documentation for more information.
+ //
+ // node: String|DomNode
+ // The node to associate data with
+ //
+ // key: Object|String?
+ // If an object, act as a setter and iterate over said object setting data items as defined.
+ // If a string, and `value` present, set the data for defined `key` to `value`
+ // If a string, and `value` absent, act as a getter, returning the data associated with said `key`
+ //
+ // value: Anything?
+ // The value to set for said `key`, provided `key` is a string (and not an object)
+ //
+ var pid = dopid(node), r;
+ if(!dataCache[pid]){ dataCache[pid] = {}; }
+
+ // API discrepency: calling with only a node returns the whole object. $.data throws
+ if(arguments.length == 1){ r = dataCache[pid]; }
+ if(typeof key == "string"){
+ // either getter or setter, based on `value` presence
+ if(arguments.length > 2){
+ dataCache[pid][key] = value;
+ }else{
+ r = dataCache[pid][key];
+ }
+ }else{
+ // must be a setter, mix `value` into data hash
+ // API discrepency: using object as setter works here
+ r = lang.mixin(dataCache[pid], key);
+ }
+
+ return r; // Object|Anything|Nothing
+ };
+
+ var removeData = dojo._removeNodeData = function(node, key){
+ // summary:
+ // Remove some data from this node
+ // node: String|DomNode
+ // The node reference to remove data from
+ // key: String?
+ // If omitted, remove all data in this dataset.
+ // If passed, remove only the passed `key` in the associated dataset
+ var pid = dopid(node);
+ if(dataCache[pid]){
+ if(key){
+ delete dataCache[pid][key];
+ }else{
+ delete dataCache[pid];
+ }
+ }
+ };
+
+ NodeList._gcNodeData = dojo._gcNodeData = function(){
+ // summary:
+ // super expensive: GC all data in the data for nodes that no longer exist in the dom.
+ // description:
+ // super expensive: GC all data in the data for nodes that no longer exist in the dom.
+ // MUCH safer to do this yourself, manually, on a per-node basis (via `NodeList.removeData()`)
+ // provided as a stop-gap for exceptionally large/complex applications with constantly changing
+ // content regions (eg: a dijit/layout/ContentPane with replacing data)
+ // There is NO automatic GC going on. If you dojo.destroy() a node, you should _removeNodeData
+ // prior to destruction.
+ var livePids = query("[" + dataattr + "]").map(dopid);
+ for(var i in dataCache){
+ if(array.indexOf(livePids, i) < 0){ delete dataCache[i]; }
+ }
+ };
+
+ // make nodeData and removeNodeData public on dojo/NodeList:
+ lang.extend(NodeList, {
+ data: NodeList._adaptWithCondition(dodata, function(a){
+ return a.length === 0 || a.length == 1 && (typeof a[0] == "string");
+ }),
+ removeData: NodeList._adaptAsForEach(removeData)
+ });
+
+ /*=====
+ lang.extend(NodeList, {
+ data: function(key, value){
+ // summary:
+ // stash or get some arbitrary data on/from these nodes.
+ //
+ // description:
+ // Stash or get some arbitrary data on/from these nodes. This private _data function is
+ // exposed publicly on `dojo/NodeList`, eg: as the result of a `dojo.query` call.
+ // DIFFERS from jQuery.data in that when used as a getter, the entire list is ALWAYS
+ // returned. EVEN WHEN THE LIST IS length == 1.
+ //
+ // A single-node version of this function is provided as `dojo._nodeData`, which follows
+ // the same signature, though expects a String ID or DomNode reference in the first
+ // position, before key/value arguments.
+ //
+ // node: String|DomNode
+ // The node to associate data with
+ //
+ // key: Object|String?
+ // If an object, act as a setter and iterate over said object setting data items as defined.
+ // If a string, and `value` present, set the data for defined `key` to `value`
+ // If a string, and `value` absent, act as a getter, returning the data associated with said `key`
+ //
+ // value: Anything?
+ // The value to set for said `key`, provided `key` is a string (and not an object)
+ //
+ // example:
+ // Set a key `bar` to some data, then retrieve it.
+ // | dojo.query(".foo").data("bar", "touched");
+ // | var touched = dojo.query(".foo").data("bar");
+ // | if(touched[0] == "touched"){ alert('win'); }
+ //
+ // example:
+ // Get all the data items for a given node.
+ // | var list = dojo.query(".foo").data();
+ // | var first = list[0];
+ //
+ // example:
+ // Set the data to a complex hash. Overwrites existing keys with new value
+ // | dojo.query(".foo").data({ bar:"baz", foo:"bar" });
+ // Then get some random key:
+ // | dojo.query(".foo").data("foo"); // returns [`bar`]
+ //
+ // returns: Object|Anything|Nothing
+ // When used as a setter via `dojo/NodeList`, a NodeList instance is returned
+ // for further chaining. When used as a getter via `dojo/NodeList` an ARRAY
+ // of items is returned. The items in the array correspond to the elements
+ // in the original list. This is true even when the list length is 1, eg:
+ // when looking up a node by ID (#foo)
+ },
+
+ removeData: function(key){
+ // summary:
+ // Remove the data associated with these nodes.
+ // key: String?
+ // If omitted, clean all data for this node.
+ // If passed, remove the data item found at `key`
+ }
+ });
+ =====*/
+
+// TODO: this is the basic implementation of adaptWithConditionAndWhenMappedConsiderLength, for lack of a better API name
+// it conflicts with the the `dojo/NodeList` way: always always return an arrayLike thinger. Consider for 2.0:
+//
+// NodeList.prototype.data = function(key, value){
+// var a = arguments, r;
+// if(a.length === 0 || a.length == 1 && (typeof a[0] == "string")){
+// r = this.map(function(node){
+// return d._data(node, key);
+// });
+// if(r.length == 1){ r = r[0]; } // the offending line, and the diff on adaptWithCondition
+// }else{
+// r = this.forEach(function(node){
+// d._data(node, key, value);
+// });
+// }
+// return r; // NodeList|Array|SingleItem
+// };
+
+ return NodeList;
+
+});
diff --git a/src/main/resources/static/dojo/NodeList-dom.js b/src/main/resources/static/dojo/NodeList-dom.js
new file mode 100644
index 0000000000000000000000000000000000000000..87ee7b73026340cc39425ef24d4f10fa885c4797
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-dom.js
@@ -0,0 +1,514 @@
+define(["./_base/kernel", "./query", "./_base/array", "./_base/lang", "./dom-class", "./dom-construct", "./dom-geometry", "./dom-attr", "./dom-style"], function(dojo, query, array, lang, domCls, domCtr, domGeom, domAttr, domStyle){
+
+ // module:
+ // dojo/NodeList-dom.js
+
+ /*=====
+ return function(){
+ // summary:
+ // Adds DOM related methods to NodeList, and returns NodeList constructor.
+ };
+ =====*/
+
+ var magicGuard = function(a){
+ // summary:
+ // the guard function for dojo.attr() and dojo.style()
+ return a.length == 1 && (typeof a[0] == "string"); // inline'd type check
+ };
+
+ var orphan = function(node){
+ // summary:
+ // function to orphan nodes
+ var p = node.parentNode;
+ if(p){
+ p.removeChild(node);
+ }
+ };
+ // FIXME: should we move orphan() to dojo.html?
+
+ var NodeList = query.NodeList,
+ awc = NodeList._adaptWithCondition,
+ aafe = NodeList._adaptAsForEach,
+ aam = NodeList._adaptAsMap;
+
+ function getSet(module){
+ return function(node, name, value){
+ if(arguments.length == 2){
+ return module[typeof name == "string" ? "get" : "set"](node, name);
+ }
+ // setter
+ return module.set(node, name, value);
+ };
+ }
+
+ lang.extend(NodeList, {
+ _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){
+ // summary:
+ // normalizes data to an array of items to insert.
+ // description:
+ // If content is an object, it can have special properties "template" and
+ // "parse". If "template" is defined, then the template value is run through
+ // dojo.string.substitute (if dojo/string.substitute() has been dojo.required elsewhere),
+ // or if templateFunc is a function on the content, that function will be used to
+ // transform the template into a final string to be used for for passing to dojo/dom-construct.toDom().
+ // If content.parse is true, then it is remembered for later, for when the content
+ // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets
+ // (if dojo.parser has been dojo.required elsewhere).
+
+ //Wanted to just use a DocumentFragment, but for the array/NodeList
+ //case that meant using cloneNode, but we may not want that.
+ //Cloning should only happen if the node operations span
+ //multiple refNodes. Also, need a real array, not a NodeList from the
+ //DOM since the node movements could change those NodeLists.
+
+ var parse = content.parse === true;
+
+ //Do we have an object that needs to be run through a template?
+ if(typeof content.template == "string"){
+ var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute);
+ content = templateFunc ? templateFunc(content.template, content) : content;
+ }
+
+ var type = (typeof content);
+ if(type == "string" || type == "number"){
+ content = domCtr.toDom(content, (refNode && refNode.ownerDocument));
+ if(content.nodeType == 11){
+ //DocumentFragment. It cannot handle cloneNode calls, so pull out the children.
+ content = lang._toArray(content.childNodes);
+ }else{
+ content = [content];
+ }
+ }else if(!lang.isArrayLike(content)){
+ content = [content];
+ }else if(!lang.isArray(content)){
+ //To get to this point, content is array-like, but
+ //not an array, which likely means a DOM NodeList. Convert it now.
+ content = lang._toArray(content);
+ }
+
+ //Pass around the parse info
+ if(parse){
+ content._runParse = true;
+ }
+ return content; //Array
+ },
+
+ _cloneNode: function(/*DOMNode*/ node){
+ // summary:
+ // private utility to clone a node. Not very interesting in the vanilla
+ // dojo/NodeList case, but delegates could do interesting things like
+ // clone event handlers if that is derivable from the node.
+ return node.cloneNode(true);
+ },
+
+ _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){
+ // summary:
+ // private utility to handle placing an array of nodes relative to another node.
+ // description:
+ // Allows for cloning the nodes in the array, and for
+ // optionally parsing widgets, if ary._runParse is true.
+
+ //Avoid a disallowed operation if trying to do an innerHTML on a non-element node.
+ if(refNode.nodeType != 1 && position == "only"){
+ return;
+ }
+ var rNode = refNode, tempNode;
+
+ //Always cycle backwards in case the array is really a
+ //DOM NodeList and the DOM operations take it out of the live collection.
+ var length = ary.length;
+ for(var i = length - 1; i >= 0; i--){
+ var node = (useClone ? this._cloneNode(ary[i]) : ary[i]);
+
+ //If need widget parsing, use a temp node, instead of waiting after inserting into
+ //real DOM because we need to start widget parsing at one node up from current node,
+ //which could cause some already parsed widgets to be parsed again.
+ if(ary._runParse && dojo.parser && dojo.parser.parse){
+ if(!tempNode){
+ tempNode = rNode.ownerDocument.createElement("div");
+ }
+ tempNode.appendChild(node);
+ dojo.parser.parse(tempNode);
+ node = tempNode.firstChild;
+ while(tempNode.firstChild){
+ tempNode.removeChild(tempNode.firstChild);
+ }
+ }
+
+ if(i == length - 1){
+ domCtr.place(node, rNode, position);
+ }else{
+ rNode.parentNode.insertBefore(node, rNode);
+ }
+ rNode = node;
+ }
+ },
+
+
+ position: aam(domGeom.position),
+ /*=====
+ position: function(){
+ // summary:
+ // Returns border-box objects (x/y/w/h) of all elements in a node list
+ // as an Array (*not* a NodeList). Acts like `dojo.position`, though
+ // assumes the node passed is each node in this list.
+
+ return dojo.map(this, dojo.position); // Array
+ },
+ =====*/
+
+ attr: awc(getSet(domAttr), magicGuard),
+ /*=====
+ attr: function(property, value){
+ // summary:
+ // gets or sets the DOM attribute for every element in the
+ // NodeList. See also `dojo.attr`
+ // property: String
+ // the attribute to get/set
+ // value: String?
+ // optional. The value to set the property to
+ // returns:
+ // if no value is passed, the result is an array of attribute values
+ // If a value is passed, the return is this NodeList
+ // example:
+ // Make all nodes with a particular class focusable:
+ // | dojo.query(".focusable").attr("tabIndex", -1);
+ // example:
+ // Disable a group of buttons:
+ // | dojo.query("button.group").attr("disabled", true);
+ // example:
+ // innerHTML can be assigned or retrieved as well:
+ // | // get the innerHTML (as an array) for each list item
+ // | var ih = dojo.query("li.replaceable").attr("innerHTML");
+ return; // dojo/NodeList|Array
+ },
+ =====*/
+
+ style: awc(getSet(domStyle), magicGuard),
+ /*=====
+ style: function(property, value){
+ // summary:
+ // gets or sets the CSS property for every element in the NodeList
+ // property: String
+ // the CSS property to get/set, in JavaScript notation
+ // ("lineHieght" instead of "line-height")
+ // value: String?
+ // optional. The value to set the property to
+ // returns:
+ // if no value is passed, the result is an array of strings.
+ // If a value is passed, the return is this NodeList
+ return; // dojo/NodeList
+ return; // Array
+ },
+ =====*/
+
+ addClass: aafe(domCls.add),
+ /*=====
+ addClass: function(className){
+ // summary:
+ // adds the specified class to every node in the list
+ // className: String|Array
+ // A String class name to add, or several space-separated class names,
+ // or an array of class names.
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ removeClass: aafe(domCls.remove),
+ /*=====
+ removeClass: function(className){
+ // summary:
+ // removes the specified class from every node in the list
+ // className: String|Array?
+ // An optional String class name to remove, or several space-separated
+ // class names, or an array of class names. If omitted, all class names
+ // will be deleted.
+ // returns:
+ // this list
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ toggleClass: aafe(domCls.toggle),
+ /*=====
+ toggleClass: function(className, condition){
+ // summary:
+ // Adds a class to node if not present, or removes if present.
+ // Pass a boolean condition if you want to explicitly add or remove.
+ // condition: Boolean?
+ // If passed, true means to add the class, false means to remove.
+ // className: String
+ // the CSS class to add
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ replaceClass: aafe(domCls.replace),
+ /*=====
+ replaceClass: function(addClassStr, removeClassStr){
+ // summary:
+ // Replaces one or more classes on a node if not present.
+ // Operates more quickly than calling `removeClass()` and `addClass()`
+ // addClassStr: String|Array
+ // A String class name to add, or several space-separated class names,
+ // or an array of class names.
+ // removeClassStr: String|Array?
+ // A String class name to remove, or several space-separated class names,
+ // or an array of class names.
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ empty: aafe(domCtr.empty),
+ /*=====
+ empty: function(){
+ // summary:
+ // clears all content from each node in the list. Effectively
+ // equivalent to removing all child nodes from every item in
+ // the list.
+ return this.forEach("item.innerHTML='';"); // dojo/NodeList
+ // FIXME: should we be checking for and/or disposing of widgets below these nodes?
+ },
+ =====*/
+
+ removeAttr: aafe(domAttr.remove),
+ /*=====
+ removeAttr: function(name){
+ // summary:
+ // Removes an attribute from each node in the list.
+ // name: String
+ // the name of the attribute to remove
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ marginBox: aam(domGeom.getMarginBox),
+ /*=====
+ marginBox: function(){
+ // summary:
+ // Returns margin-box size of nodes
+ return; // dojo/NodeList
+ },
+ =====*/
+
+ // FIXME: connectPublisher()? connectRunOnce()?
+
+ /*
+ destroy: function(){
+ // summary:
+ // destroys every item in the list.
+ this.forEach(d.destroy);
+ // FIXME: should we be checking for and/or disposing of widgets below these nodes?
+ },
+ */
+
+ place: function(/*String||Node*/ queryOrNode, /*String*/ position){
+ // summary:
+ // places elements of this node list relative to the first element matched
+ // by queryOrNode. Returns the original NodeList. See: `dojo.place`
+ // queryOrNode:
+ // may be a string representing any valid CSS3 selector or a DOM node.
+ // In the selector case, only the first matching element will be used
+ // for relative positioning.
+ // position:
+ // can be one of:
+ //
+ // - "last" (default)
+ // - "first"
+ // - "before"
+ // - "after"
+ // - "only"
+ // - "replace"
+ //
+ // or an offset in the childNodes property
+ var item = query(queryOrNode)[0];
+ return this.forEach(function(node){ domCtr.place(node, item, position); }); // dojo/NodeList
+ },
+
+ orphan: function(/*String?*/ filter){
+ // summary:
+ // removes elements in this list that match the filter
+ // from their parents and returns them as a new NodeList.
+ // filter:
+ // CSS selector like ".foo" or "div > span"
+ // returns:
+ // NodeList containing the orphaned elements
+ return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo/NodeList
+ },
+
+ adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
+ // summary:
+ // places any/all elements in queryOrListOrNode at a
+ // position relative to the first element in this list.
+ // Returns a dojo/NodeList of the adopted elements.
+ // queryOrListOrNode:
+ // a DOM node or a query string or a query result.
+ // Represents the nodes to be adopted relative to the
+ // first element of this NodeList.
+ // position:
+ // can be one of:
+ //
+ // - "last" (default)
+ // - "first"
+ // - "before"
+ // - "after"
+ // - "only"
+ // - "replace"
+ //
+ // or an offset in the childNodes property
+ return query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo/NodeList
+ },
+
+ // FIXME: do we need this?
+ query: function(/*String*/ queryStr){
+ // summary:
+ // Returns a new list whose members match the passed query,
+ // assuming elements of the current NodeList as the root for
+ // each search.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
+ // | bacon is tasty, dontcha think?
+ // |
+ // |
+ // |
+ // |
great comedians may not be funny in person
+ // |
+ // If we are presented with the following definition for a NodeList:
+ // | var l = new NodeList(dojo.byId("foo"), dojo.byId("bar"));
+ // it's possible to find all span elements under paragraphs
+ // contained by these elements with this sub-query:
+ // | var spans = l.query("p span");
+
+ // FIXME: probably slow
+ if(!queryStr){ return this; }
+ var ret = new NodeList;
+ this.map(function(node){
+ // FIXME: why would we ever get undefined here?
+ query(queryStr, node).forEach(function(subNode){
+ if(subNode !== undefined){
+ ret.push(subNode);
+ }
+ });
+ });
+ return ret._stash(this); // dojo/NodeList
+ },
+
+ filter: function(/*String|Function*/ filter){
+ // summary:
+ // "masks" the built-in javascript filter() method (supported
+ // in Dojo via `dojo.filter`) to support passing a simple
+ // string filter in addition to supporting filtering function
+ // objects.
+ // filter:
+ // If a string, a CSS rule like ".thinger" or "div > span".
+ // example:
+ // "regular" JS filter syntax as exposed in dojo.filter:
+ // | dojo.query("*").filter(function(item){
+ // | // highlight every paragraph
+ // | return (item.nodeName == "p");
+ // | }).style("backgroundColor", "yellow");
+ // example:
+ // the same filtering using a CSS selector
+ // | dojo.query("*").filter("p").styles("backgroundColor", "yellow");
+
+ var a = arguments, items = this, start = 0;
+ if(typeof filter == "string"){ // inline'd type check
+ items = query._filterResult(this, a[0]);
+ if(a.length == 1){
+ // if we only got a string query, pass back the filtered results
+ return items._stash(this); // dojo/NodeList
+ }
+ // if we got a callback, run it over the filtered items
+ start = 1;
+ }
+ return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo/NodeList
+ },
+
+ /*
+ // FIXME: should this be "copyTo" and include parenting info?
+ clone: function(){
+ // summary:
+ // creates node clones of each element of this list
+ // and returns a new list containing the clones
+ },
+ */
+
+ addContent: function(/*String||DomNode||Object||dojo/NodeList*/ content, /*String||Integer?*/ position){
+ // summary:
+ // add a node, NodeList or some HTML as a string to every item in the
+ // list. Returns the original list.
+ // description:
+ // a copy of the HTML content is added to each item in the
+ // list, with an optional position argument. If no position
+ // argument is provided, the content is appended to the end of
+ // each item.
+ // content:
+ // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or
+ // NodeList, the content will be cloned if the current NodeList has more than one
+ // element. Only the DOM nodes are cloned, no event handlers. If it is an Object,
+ // it should be an object with at "template" String property that has the HTML string
+ // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute
+ // will be used on the "template" to generate the final HTML string. Other allowed
+ // properties on the object are: "parse" if the HTML
+ // string should be parsed for widgets (dojo.require("dojo.parser") to get that
+ // option to work), and "templateFunc" if a template function besides dojo.string.substitute
+ // should be used to transform the "template".
+ // position:
+ // can be one of:
+ //
+ // - "last"||"end" (default)
+ // - "first||"start"
+ // - "before"
+ // - "after"
+ // - "replace" (replaces nodes in this NodeList with new content)
+ // - "only" (removes other children of the nodes so new content is the only child)
+ //
+ // or an offset in the childNodes property
+ // example:
+ // appends content to the end if the position is omitted
+ // | dojo.query("h3 > p").addContent("hey there!");
+ // example:
+ // add something to the front of each element that has a
+ // "thinger" property:
+ // | dojo.query("[thinger]").addContent("...", "first");
+ // example:
+ // adds a header before each element of the list
+ // | dojo.query(".note").addContent("
NOTE:
", "before");
+ // example:
+ // add a clone of a DOM node to the end of every element in
+ // the list, removing it from its existing parent.
+ // | dojo.query(".note").addContent(dojo.byId("foo"));
+ // example:
+ // Append nodes from a templatized string.
+ // | dojo.require("dojo.string");
+ // | dojo.query(".note").addContent({
+ // | template: '${id}: ${name}',
+ // | id: "user332",
+ // | name: "Mr. Anderson"
+ // | });
+ // example:
+ // Append nodes from a templatized string that also has widgets parsed.
+ // | dojo.require("dojo.string");
+ // | dojo.require("dojo.parser");
+ // | var notes = dojo.query(".note").addContent({
+ // | template: '',
+ // | parse: true,
+ // | text: "Send"
+ // | });
+ content = this._normalize(content, this[0]);
+ for(var i = 0, node; (node = this[i]); i++){
+ if(content.length){
+ this._place(content, node, position, i > 0);
+ }else{
+ // if it is an empty array, we empty the target node
+ domCtr.empty(node);
+ }
+ }
+ return this; // dojo/NodeList
+ }
+ });
+
+ return NodeList;
+});
diff --git a/src/main/resources/static/dojo/NodeList-fx.js b/src/main/resources/static/dojo/NodeList-fx.js
new file mode 100644
index 0000000000000000000000000000000000000000..882aa3dc49cf99b4111e1edea456caa4ada1c54c
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-fx.js
@@ -0,0 +1,221 @@
+define(["./query", "./_base/lang", "./aspect", "./_base/fx", "./fx"],
+function(query, lang, aspect, baseFx, coreFx){
+
+// module:
+// dojo/NodeList-fx
+
+/*=====
+return function(){
+ // summary:
+ // Adds dojo.fx animation support to dojo.query() by extending the NodeList class
+ // with additional FX functions. NodeList is the array-like object used to hold query results.
+};
+=====*/
+
+var NodeList = query.NodeList;
+
+lang.extend(NodeList, {
+ _anim: function(obj, method, args){
+ args = args||{};
+ var a = coreFx.combine(
+ this.map(function(item){
+ var tmpArgs = { node: item };
+ lang.mixin(tmpArgs, args);
+ return obj[method](tmpArgs);
+ })
+ );
+ return args.auto ? a.play() && this : a; // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ wipeIn: function(args){
+ // summary:
+ // wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // Fade in all tables with class "blah":
+ // | dojo.query("table.blah").wipeIn().play();
+ //
+ // example:
+ // Utilizing `auto` to get the NodeList back:
+ // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
+ //
+ return this._anim(coreFx, "wipeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ wipeOut: function(args){
+ // summary:
+ // wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // Wipe out all tables with class "blah":
+ // | dojo.query("table.blah").wipeOut().play();
+ return this._anim(coreFx, "wipeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ slideTo: function(args){
+ // summary:
+ // slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // | Move all tables with class "blah" to 300/300:
+ // | dojo.query("table.blah").slideTo({
+ // | left: 40,
+ // | top: 50
+ // | }).play();
+ return this._anim(coreFx, "slideTo", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+
+ fadeIn: function(args){
+ // summary:
+ // fade in all elements of this NodeList via `dojo.fadeIn`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // Fade in all tables with class "blah":
+ // | dojo.query("table.blah").fadeIn().play();
+ return this._anim(baseFx, "fadeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ fadeOut: function(args){
+ // summary:
+ // fade out all elements of this NodeList via `dojo.fadeOut`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // Fade out all elements with class "zork":
+ // | dojo.query(".zork").fadeOut().play();
+ // example:
+ // Fade them on a delay and do something at the end:
+ // | var fo = dojo.query(".zork").fadeOut();
+ // | aspect.after(fo, "onEnd", function(){ /*...*/ }, true);
+ // | fo.play();
+ // example:
+ // Using `auto`:
+ // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
+ //
+ return this._anim(baseFx, "fadeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ animateProperty: function(args){
+ // summary:
+ // Animate all elements of this NodeList across the properties specified.
+ // syntax identical to `dojo.animateProperty`
+ //
+ // args: Object?
+ // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
+ // an `auto` parameter.
+ //
+ // returns: dojo/_base/fx.Animation|dojo/NodeList
+ // A special args member `auto` can be passed to automatically play the animation.
+ // If args.auto is present, the original dojo/NodeList will be returned for further
+ // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
+ //
+ // example:
+ // | dojo.query(".zork").animateProperty({
+ // | duration: 500,
+ // | properties: {
+ // | color: { start: "black", end: "white" },
+ // | left: { end: 300 }
+ // | }
+ // | }).play();
+ //
+ // example:
+ // | dojo.query(".grue").animateProperty({
+ // | auto:true,
+ // | properties: {
+ // | height:240
+ // | }
+ // | }).onclick(handler);
+ return this._anim(baseFx, "animateProperty", args); // dojo/_base/fx.Animation|dojo/NodeList
+ },
+
+ anim: function( /*Object*/ properties,
+ /*Integer?*/ duration,
+ /*Function?*/ easing,
+ /*Function?*/ onEnd,
+ /*Integer?*/ delay){
+ // summary:
+ // Animate one or more CSS properties for all nodes in this list.
+ // The returned animation object will already be playing when it
+ // is returned. See the docs for `dojo.anim` for full details.
+ // properties: Object
+ // the properties to animate. does NOT support the `auto` parameter like other
+ // NodeList-fx methods.
+ // duration: Integer?
+ // Optional. The time to run the animations for
+ // easing: Function?
+ // Optional. The easing function to use.
+ // onEnd: Function?
+ // A function to be called when the animation ends
+ // delay:
+ // how long to delay playing the returned animation
+ // example:
+ // Another way to fade out:
+ // | dojo.query(".thinger").anim({ opacity: 0 });
+ // example:
+ // animate all elements with the "thigner" class to a width of 500
+ // pixels over half a second
+ // | dojo.query(".thinger").anim({ width: 500 }, 700);
+ var canim = coreFx.combine(
+ this.map(function(item){
+ return baseFx.animateProperty({
+ node: item,
+ properties: properties,
+ duration: duration||350,
+ easing: easing
+ });
+ })
+ );
+ if(onEnd){
+ aspect.after(canim, "onEnd", onEnd, true);
+ }
+ return canim.play(delay||0); // dojo/_base/fx.Animation
+ }
+});
+
+return NodeList;
+});
diff --git a/src/main/resources/static/dojo/NodeList-html.js b/src/main/resources/static/dojo/NodeList-html.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc5cc3d1a9ef88f542bd7175a2fba573c37bf54a
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-html.js
@@ -0,0 +1,53 @@
+define(["./query", "./_base/lang", "./html"], function(query, lang, html){
+
+// module:
+// dojo/NodeList-html
+
+/*=====
+return function(){
+ // summary:
+ // Adds a chainable html method to dojo.query() / NodeList instances for setting/replacing node content
+};
+=====*/
+
+var NodeList = query.NodeList;
+
+
+lang.extend(NodeList, {
+ html: function(/* String|DomNode|NodeList? */ content, /* Object? */params){
+ // summary:
+ // see `dojo/html.set()`. Set the content of all elements of this NodeList
+ //
+ // content:
+ // An html string, node or enumerable list of nodes for insertion into the dom
+ //
+ // params:
+ // Optional flags/properties to configure the content-setting. See dojo/html._ContentSetter
+ //
+ // description:
+ // Based around `dojo/html.set()`, set the content of the Elements in a
+ // NodeList to the given content (string/node/nodelist), with optional arguments
+ // to further tune the set content behavior.
+ //
+ // example:
+ // | query(".thingList").html("
1
2
3
",
+ // | {
+ // | parseContent: true,
+ // | onBegin: function(){
+ // | this.content = this.content.replace(/([0-9])/g, this.id + ": $1");
+ // | this.inherited("onBegin", arguments);
+ // | }
+ // | }).removeClass("notdone").addClass("done");
+
+ var dhs = new html._ContentSetter(params || {});
+ this.forEach(function(elm){
+ dhs.node = elm;
+ dhs.set(content);
+ dhs.tearDown();
+ });
+ return this; // dojo/NodeList
+ }
+});
+
+return NodeList;
+});
diff --git a/src/main/resources/static/dojo/NodeList-manipulate.js b/src/main/resources/static/dojo/NodeList-manipulate.js
new file mode 100644
index 0000000000000000000000000000000000000000..eb36ccb7b38d1949dacf2d7328816a4f51cb2ad9
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-manipulate.js
@@ -0,0 +1,724 @@
+define(["./query", "./_base/lang", "./_base/array", "./dom-construct", "./NodeList-dom"], function(dquery, lang, array, construct){
+ // module:
+ // dojo/NodeList-manipulate
+
+ /*=====
+ return function(){
+ // summary:
+ // Adds chainable methods to dojo.query() / NodeList instances for manipulating HTML
+ // and DOM nodes and their properties.
+ };
+ =====*/
+
+ var NodeList = dquery.NodeList;
+
+ //TODO: add a way to parse for widgets in the injected markup?
+
+ function getText(/*DOMNode*/node){
+ // summary:
+ // recursion method for text() to use. Gets text value for a node.
+ // description:
+ // Juse uses nodedValue so things like tags do not end up in
+ // the text as any sort of line return.
+ var text = "", ch = node.childNodes;
+ for(var i = 0, n; n = ch[i]; i++){
+ //Skip comments.
+ if(n.nodeType != 8){
+ if(n.nodeType == 1){
+ text += getText(n);
+ }else{
+ text += n.nodeValue;
+ }
+ }
+ }
+ return text;
+ }
+
+ function getWrapInsertion(/*DOMNode*/node){
+ // summary:
+ // finds the innermost element to use for wrap insertion.
+
+ //Make it easy, assume single nesting, no siblings.
+ while(node.childNodes[0] && node.childNodes[0].nodeType == 1){
+ node = node.childNodes[0];
+ }
+ return node; //DOMNode
+ }
+
+ function makeWrapNode(/*DOMNode||String*/html, /*DOMNode*/refNode){
+ // summary:
+ // convert HTML into nodes if it is not already a node.
+ if(typeof html == "string"){
+ html = construct.toDom(html, (refNode && refNode.ownerDocument));
+ if(html.nodeType == 11){
+ //DocumentFragment cannot handle cloneNode, so choose first child.
+ html = html.childNodes[0];
+ }
+ }else if(html.nodeType == 1 && html.parentNode){
+ //This element is already in the DOM clone it, but not its children.
+ html = html.cloneNode(false);
+ }
+ return html; /*DOMNode*/
+ }
+
+ lang.extend(NodeList, {
+ _placeMultiple: function(/*String||Node||NodeList*/query, /*String*/position){
+ // summary:
+ // private method for inserting queried nodes into all nodes in this NodeList
+ // at different positions. Differs from NodeList.place because it will clone
+ // the nodes in this NodeList if the query matches more than one element.
+ var nl2 = typeof query == "string" || query.nodeType ? dquery(query) : query;
+ var toAdd = [];
+ for(var i = 0; i < nl2.length; i++){
+ //Go backwards in DOM to make dom insertions easier via insertBefore
+ var refNode = nl2[i];
+ var length = this.length;
+ for(var j = length - 1, item; item = this[j]; j--){
+ if(i > 0){
+ //Need to clone the item. This also means
+ //it needs to be added to the current NodeList
+ //so it can also be the target of other chaining operations.
+ item = this._cloneNode(item);
+ toAdd.unshift(item);
+ }
+ if(j == length - 1){
+ construct.place(item, refNode, position);
+ }else{
+ refNode.parentNode.insertBefore(item, refNode);
+ }
+ refNode = item;
+ }
+ }
+
+ if(toAdd.length){
+ //Add the toAdd items to the current NodeList. Build up list of args
+ //to pass to splice.
+ toAdd.unshift(0);
+ toAdd.unshift(this.length - 1);
+ Array.prototype.splice.apply(this, toAdd);
+ }
+
+ return this; // dojo/NodeList
+ },
+
+ innerHTML: function(/*String|DOMNode|NodeList?*/ value){
+ // summary:
+ // allows setting the innerHTML of each node in the NodeList,
+ // if there is a value passed in, otherwise, reads the innerHTML value of the first node.
+ // description:
+ // This method is simpler than the dojo/NodeList.html() method provided by
+ // `dojo/NodeList-html`. This method just does proper innerHTML insertion of HTML fragments,
+ // and it allows for the innerHTML to be read for the first node in the node list.
+ // Since dojo/NodeList-html already took the "html" name, this method is called
+ // "innerHTML". However, if dojo/NodeList-html has not been loaded yet, this
+ // module will define an "html" method that can be used instead. Be careful if you
+ // are working in an environment where it is possible that dojo/NodeList-html could
+ // have been loaded, since its definition of "html" will take precedence.
+ // The nodes represented by the value argument will be cloned if more than one
+ // node is in this NodeList. The nodes in this NodeList are returned in the "set"
+ // usage of this method, not the HTML that was inserted.
+ // returns:
+ // if no value is passed, the result is String, the innerHTML of the first node.
+ // If a value is passed, the return is this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
+ // This code inserts `
Hello World
` into both divs:
+ // | dojo.query("div").innerHTML("
Hello World
");
+ // example:
+ // assume a DOM created by this markup:
+ // |
Hello Mars
+ // |
Hello World
+ // This code returns `
Hello Mars
`:
+ // | var message = dojo.query("div").innerHTML();
+ if(arguments.length){
+ return this.addContent(value, "only"); // dojo/NodeList
+ }else{
+ return this[0].innerHTML; //String
+ }
+ },
+
+ /*=====
+ html: function(value){
+ // summary:
+ // see the information for "innerHTML". "html" is an alias for "innerHTML", but is
+ // only defined if dojo/NodeList-html has not been loaded.
+ // description:
+ // An alias for the "innerHTML" method, but only defined if there is not an existing
+ // "html" method on dojo/NodeList. Be careful if you are working in an environment
+ // where it is possible that dojo/NodeList-html could have been loaded, since its
+ // definition of "html" will take precedence. If you are not sure if dojo/NodeList-html
+ // could be loaded, use the "innerHTML" method.
+ // value: String|DOMNode|NodeList?
+ // The HTML fragment to use as innerHTML. If value is not passed, then the innerHTML
+ // of the first element in this NodeList is returned.
+ // returns:
+ // if no value is passed, the result is String, the innerHTML of the first node.
+ // If a value is passed, the return is this dojo/NodeList
+ return; // dojo/NodeList|String
+ },
+ =====*/
+
+ text: function(/*String*/value){
+ // summary:
+ // allows setting the text value of each node in the NodeList,
+ // if there is a value passed in, otherwise, returns the text value for all the
+ // nodes in the NodeList in one string.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
+ // This code inserts "Hello World" into both divs:
+ // | dojo.query("div").text("Hello World");
+ // example:
+ // assume a DOM created by this markup:
+ // |
Hello Mars today
+ // |
Hello World
+ // This code returns "Hello Mars today":
+ // | var message = dojo.query("div").text();
+ // returns:
+ // if no value is passed, the result is String, the text value of the first node.
+ // If a value is passed, the return is this dojo/NodeList
+ if(arguments.length){
+ for(var i = 0, node; node = this[i]; i++){
+ if(node.nodeType == 1){
+ construct.empty(node);
+ node.appendChild(node.ownerDocument.createTextNode(value));
+ }
+ }
+ return this; // dojo/NodeList
+ }else{
+ var result = "";
+ for(i = 0; node = this[i]; i++){
+ result += getText(node);
+ }
+ return result; //String
+ }
+ },
+
+ val: function(/*String||Array*/value){
+ // summary:
+ // If a value is passed, allows seting the value property of form elements in this
+ // NodeList, or properly selecting/checking the right value for radio/checkbox/select
+ // elements. If no value is passed, the value of the first node in this NodeList
+ // is returned.
+ // returns:
+ // if no value is passed, the result is String or an Array, for the value of the
+ // first node.
+ // If a value is passed, the return is this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
+ // This code gets and sets the values for the form fields above:
+ // | dojo.query('[type="text"]').val(); //gets value foo
+ // | dojo.query('[type="text"]').val("bar"); //sets the input's value to "bar"
+ // | dojo.query("select").val() //gets array value ["red", "yellow"]
+ // | dojo.query("select").val(["blue", "yellow"]) //Sets the blue and yellow options to selected.
+
+ //Special work for input elements.
+ if(arguments.length){
+ var isArray = lang.isArray(value);
+ for(var index = 0, node; node = this[index]; index++){
+ var name = node.nodeName.toUpperCase();
+ var type = node.type;
+ var newValue = isArray ? value[index] : value;
+
+ if(name == "SELECT"){
+ var opts = node.options;
+ for(var i = 0; i < opts.length; i++){
+ var opt = opts[i];
+ if(node.multiple){
+ opt.selected = (array.indexOf(value, opt.value) != -1);
+ }else{
+ opt.selected = (opt.value == newValue);
+ }
+ }
+ }else if(type == "checkbox" || type == "radio"){
+ node.checked = (node.value == newValue);
+ }else{
+ node.value = newValue;
+ }
+ }
+ return this; // dojo/NodeList
+ }else{
+ //node already declared above.
+ node = this[0];
+ if(!node || node.nodeType != 1){
+ return undefined;
+ }
+ value = node.value || "";
+ if(node.nodeName.toUpperCase() == "SELECT" && node.multiple){
+ //A multivalued selectbox. Do the pain.
+ value = [];
+ //opts declared above in if block.
+ opts = node.options;
+ //i declared above in if block;
+ for(i = 0; i < opts.length; i++){
+ //opt declared above in if block
+ opt = opts[i];
+ if(opt.selected){
+ value.push(opt.value);
+ }
+ }
+ if(!value.length){
+ value = null;
+ }
+ }
+ return value; //String||Array
+ }
+ },
+
+ append: function(/*String||DOMNode||NodeList*/content){
+ // summary:
+ // appends the content to every node in the NodeList.
+ // description:
+ // The content will be cloned if the length of NodeList
+ // is greater than 1. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the appended content.
+ // example:
+ // assume a DOM created by this markup:
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("div").append("append");
+ // Results in this DOM structure:
+ // |
Hello Mars
append
+ // |
Hello World
append
+ return this.addContent(content, "last"); // dojo/NodeList
+ },
+
+ appendTo: function(/*String*/query){
+ // summary:
+ // appends nodes in this NodeList to the nodes matched by
+ // the query passed to appendTo.
+ // description:
+ // The nodes in this NodeList will be cloned if the query
+ // matches more than one element. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the matched nodes from the query.
+ // example:
+ // assume a DOM created by this markup:
+ // | append
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("span").appendTo("p");
+ // Results in this DOM structure:
+ // |
Hello Marsappend
+ // |
Hello Worldappend
+ return this._placeMultiple(query, "last"); // dojo/NodeList
+ },
+
+ prepend: function(/*String||DOMNode||NodeList*/content){
+ // summary:
+ // prepends the content to every node in the NodeList.
+ // description:
+ // The content will be cloned if the length of NodeList
+ // is greater than 1. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the appended content.
+ // assume a DOM created by this markup:
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("div").prepend("prepend");
+ // Results in this DOM structure:
+ // |
prepend
Hello Mars
+ // |
prepend
Hello World
+ return this.addContent(content, "first"); // dojo/NodeList
+ },
+
+ prependTo: function(/*String*/query){
+ // summary:
+ // prepends nodes in this NodeList to the nodes matched by
+ // the query passed to prependTo.
+ // description:
+ // The nodes in this NodeList will be cloned if the query
+ // matches more than one element. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the matched nodes from the query.
+ // example:
+ // assume a DOM created by this markup:
+ // | prepend
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("span").prependTo("p");
+ // Results in this DOM structure:
+ // |
prependHello Mars
+ // |
prependHello World
+ return this._placeMultiple(query, "first"); // dojo/NodeList
+ },
+
+ after: function(/*String||Element||NodeList*/content){
+ // summary:
+ // Places the content after every node in the NodeList.
+ // description:
+ // The content will be cloned if the length of NodeList
+ // is greater than 1. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the appended content.
+ // example:
+ // assume a DOM created by this markup:
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("div").after("after");
+ // Results in this DOM structure:
+ // |
Hello Mars
after
+ // |
Hello World
after
+ return this.addContent(content, "after"); // dojo/NodeList
+ },
+
+ insertAfter: function(/*String*/query){
+ // summary:
+ // The nodes in this NodeList will be placed after the nodes
+ // matched by the query passed to insertAfter.
+ // description:
+ // The nodes in this NodeList will be cloned if the query
+ // matches more than one element. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the matched nodes from the query.
+ // example:
+ // assume a DOM created by this markup:
+ // | after
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("span").insertAfter("p");
+ // Results in this DOM structure:
+ // |
Hello Mars
after
+ // |
Hello World
after
+ return this._placeMultiple(query, "after"); // dojo/NodeList
+ },
+
+ before: function(/*String||DOMNode||NodeList*/content){
+ // summary:
+ // Places the content before every node in the NodeList.
+ // description:
+ // The content will be cloned if the length of NodeList
+ // is greater than 1. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the appended content.
+ // example:
+ // assume a DOM created by this markup:
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("div").before("before");
+ // Results in this DOM structure:
+ // | before
Hello Mars
+ // | before
Hello World
+ return this.addContent(content, "before"); // dojo/NodeList
+ },
+
+ insertBefore: function(/*String*/query){
+ // summary:
+ // The nodes in this NodeList will be placed after the nodes
+ // matched by the query passed to insertAfter.
+ // description:
+ // The nodes in this NodeList will be cloned if the query
+ // matches more than one element. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // dojo/NodeList, the nodes currently in this NodeList will be returned,
+ // not the matched nodes from the query.
+ // example:
+ // assume a DOM created by this markup:
+ // | before
+ // |
Hello Mars
+ // |
Hello World
+ // Running this code:
+ // | dojo.query("span").insertBefore("p");
+ // Results in this DOM structure:
+ // | before
Hello Mars
+ // | before
Hello World
+ return this._placeMultiple(query, "before"); // dojo/NodeList
+ },
+
+ /*=====
+ remove: function(simpleFilter){
+ // summary:
+ // alias for dojo/NodeList's orphan method. Removes elements
+ // in this list that match the simple filter from their parents
+ // and returns them as a new NodeList.
+ // simpleFilter: String
+ // single-expression CSS rule. For example, ".thinger" or
+ // "#someId[attrName='value']" but not "div > span". In short,
+ // anything which does not invoke a descent to evaluate but
+ // can instead be used to test a single node is acceptable.
+
+ return; // dojo/NodeList
+ },
+ =====*/
+ remove: NodeList.prototype.orphan,
+
+ wrap: function(/*String||DOMNode*/html){
+ // summary:
+ // Wrap each node in the NodeList with html passed to wrap.
+ // description:
+ // html will be cloned if the NodeList has more than one
+ // element. Only DOM nodes are cloned, not any attached
+ // event handlers.
+ // returns:
+ // the nodes in the current NodeList will be returned,
+ // not the nodes from html argument.
+ // example:
+ // assume a DOM created by this markup:
+ // | one
+ // | two
+ // Running this code:
+ // | dojo.query("b").wrap("
");
+ // Results in this DOM structure:
+ // |
one
+ // |
two
+ if(this[0]){
+ html = makeWrapNode(html, this[0]);
+
+ //Now cycle through the elements and do the insertion.
+ for(var i = 0, node; node = this[i]; i++){
+ //Always clone because if html is used to hold one of
+ //the "this" nodes, then on the clone of html it will contain
+ //that "this" node, and that would be bad.
+ var clone = this._cloneNode(html);
+ if(node.parentNode){
+ node.parentNode.replaceChild(clone, node);
+ }
+ //Find deepest element and insert old node in it.
+ var insertion = getWrapInsertion(clone);
+ insertion.appendChild(node);
+ }
+ }
+ return this; // dojo/NodeList
+ },
+
+ wrapAll: function(/*String||DOMNode*/html){
+ // summary:
+ // Insert html where the first node in this NodeList lives, then place all
+ // nodes in this NodeList as the child of the html.
+ // returns:
+ // the nodes in the current NodeList will be returned,
+ // not the nodes from html argument.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").wrapAll('');
+ // Results in this DOM structure:
+ // |
+ // |
+ // |
Red One
+ // |
Red Two
+ // |
+ // |
Blue One
+ // |
Blue Two
+ // |
+ if(this[0]){
+ html = makeWrapNode(html, this[0]);
+
+ //Place the wrap HTML in place of the first node.
+ this[0].parentNode.replaceChild(html, this[0]);
+
+ //Now cycle through the elements and move them inside
+ //the wrap.
+ var insertion = getWrapInsertion(html);
+ for(var i = 0, node; node = this[i]; i++){
+ insertion.appendChild(node);
+ }
+ }
+ return this; // dojo/NodeList
+ },
+
+ wrapInner: function(/*String||DOMNode*/html){
+ // summary:
+ // For each node in the NodeList, wrap all its children with the passed in html.
+ // description:
+ // html will be cloned if the NodeList has more than one
+ // element. Only DOM nodes are cloned, not any attached
+ // event handlers.
+ // returns:
+ // the nodes in the current NodeList will be returned,
+ // not the nodes from html argument.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").wrapInner('');
+ // Results in this DOM structure:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ if(this[0]){
+ html = makeWrapNode(html, this[0]);
+ for(var i = 0; i < this.length; i++){
+ //Always clone because if html is used to hold one of
+ //the "this" nodes, then on the clone of html it will contain
+ //that "this" node, and that would be bad.
+ var clone = this._cloneNode(html);
+
+ //Need to convert the childNodes to an array since wrapAll modifies the
+ //DOM and can change the live childNodes NodeList.
+ this._wrap(lang._toArray(this[i].childNodes), null, this._NodeListCtor).wrapAll(clone);
+ }
+ }
+ return this; // dojo/NodeList
+ },
+
+ replaceWith: function(/*String||DOMNode||NodeList*/content){
+ // summary:
+ // Replaces each node in ths NodeList with the content passed to replaceWith.
+ // description:
+ // The content will be cloned if the length of NodeList
+ // is greater than 1. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // The nodes currently in this NodeList will be returned, not the replacing content.
+ // Note that the returned nodes have been removed from the DOM.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").replaceWith('
Green
');
+ // Results in this DOM structure:
+ // |
+ // |
Green
+ // |
Blue One
+ // |
Green
+ // |
Blue Two
+ // |
+ content = this._normalize(content, this[0]);
+ for(var i = 0, node; node = this[i]; i++){
+ this._place(content, node, "before", i > 0);
+ node.parentNode.removeChild(node);
+ }
+ return this; // dojo/NodeList
+ },
+
+ replaceAll: function(/*String*/query){
+ // summary:
+ // replaces nodes matched by the query passed to replaceAll with the nodes
+ // in this NodeList.
+ // description:
+ // The nodes in this NodeList will be cloned if the query
+ // matches more than one element. Only the DOM nodes are cloned, not
+ // any attached event handlers.
+ // returns:
+ // The nodes currently in this NodeList will be returned, not the matched nodes
+ // from the query. The nodes currently in this NodeLIst could have
+ // been cloned, so the returned NodeList will include the cloned nodes.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
___
+ // |
Red One
+ // |
___
+ // |
Blue One
+ // |
___
+ // |
Red Two
+ // |
___
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").replaceAll(".blue");
+ // Results in this DOM structure:
+ // |
+ // |
___
+ // |
___
+ // |
Red One
+ // |
Red Two
+ // |
___
+ // |
___
+ // |
Red One
+ // |
Red Two
+ // |
+ var nl = dquery(query);
+ var content = this._normalize(this, this[0]);
+ for(var i = 0, node; node = nl[i]; i++){
+ this._place(content, node, "before", i > 0);
+ node.parentNode.removeChild(node);
+ }
+ return this; // dojo/NodeList
+ },
+
+ clone: function(){
+ // summary:
+ // Clones all the nodes in this NodeList and returns them as a new NodeList.
+ // description:
+ // Only the DOM nodes are cloned, not any attached event handlers.
+ // returns:
+ // a cloned set of the original nodes.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").clone().appendTo(".container");
+ // Results in this DOM structure:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
Red One
+ // |
Red Two
+ // |
+
+ //TODO: need option to clone events?
+ var ary = [];
+ for(var i = 0; i < this.length; i++){
+ ary.push(this._cloneNode(this[i]));
+ }
+ return this._wrap(ary, this, this._NodeListCtor); // dojo/NodeList
+ }
+ });
+
+ //set up html method if one does not exist
+ if(!NodeList.prototype.html){
+ NodeList.prototype.html = NodeList.prototype.innerHTML;
+ }
+
+ return NodeList;
+});
diff --git a/src/main/resources/static/dojo/NodeList-traverse.js b/src/main/resources/static/dojo/NodeList-traverse.js
new file mode 100644
index 0000000000000000000000000000000000000000..bf36ae9335a546fb09fab504a3f1130c343baafd
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList-traverse.js
@@ -0,0 +1,503 @@
+define(["./query", "./_base/lang", "./_base/array"], function(dquery, lang, array){
+
+// module:
+// dojo/NodeList-traverse
+
+/*=====
+return function(){
+ // summary:
+ // Adds chainable methods to dojo.query() / NodeList instances for traversing the DOM
+};
+=====*/
+
+var NodeList = dquery.NodeList;
+
+lang.extend(NodeList, {
+ _buildArrayFromCallback: function(/*Function*/ callback){
+ // summary:
+ // builds a new array of possibly differing size based on the input list.
+ // Since the returned array is likely of different size than the input array,
+ // the array's map function cannot be used.
+ var ary = [];
+ for(var i = 0; i < this.length; i++){
+ var items = callback.call(this[i], this[i], ary);
+ if(items){
+ ary = ary.concat(items);
+ }
+ }
+ return ary; //Array
+ },
+
+ _getUniqueAsNodeList: function(/*Array*/ nodes){
+ // summary:
+ // given a list of nodes, make sure only unique
+ // elements are returned as our NodeList object.
+ // Does not call _stash().
+ var ary = [];
+ //Using for loop for better speed.
+ for(var i = 0, node; node = nodes[i]; i++){
+ //Should be a faster way to do this. dojo.query has a private
+ //_zip function that may be inspirational, but there are pathways
+ //in query that force nozip?
+ if(node.nodeType == 1 && array.indexOf(ary, node) == -1){
+ ary.push(node);
+ }
+ }
+ return this._wrap(ary, null, this._NodeListCtor); // dojo/NodeList
+ },
+
+ _getUniqueNodeListWithParent: function(/*Array*/ nodes, /*String*/ query){
+ // summary:
+ // gets unique element nodes, filters them further
+ // with an optional query and then calls _stash to track parent NodeList.
+ var ary = this._getUniqueAsNodeList(nodes);
+ ary = (query ? dquery._filterResult(ary, query) : ary);
+ return ary._stash(this); // dojo/NodeList
+ },
+
+ _getRelatedUniqueNodes: function(/*String?*/ query, /*Function*/ callback){
+ // summary:
+ // cycles over all the nodes and calls a callback
+ // to collect nodes for a possible inclusion in a result.
+ // The callback will get two args: callback(node, ary),
+ // where ary is the array being used to collect the nodes.
+ return this._getUniqueNodeListWithParent(this._buildArrayFromCallback(callback), query); // dojo/NodeList
+ },
+
+ children: function(/*String?*/ query){
+ // summary:
+ // Returns all immediate child elements for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the child elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // all immediate child elements for the nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".container").children();
+ // returns the four divs that are children of the container div.
+ // Running this code:
+ // | dojo.query(".container").children(".red");
+ // returns the two divs that have the class "red".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ return lang._toArray(node.childNodes);
+ }); // dojo/NodeList
+ },
+
+ closest: function(/*String*/ query, /*String|DOMNode?*/ root){
+ // summary:
+ // Returns closest parent that matches query, including current node in this
+ // dojo/NodeList if it matches the query.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // root:
+ // If specified, query is relative to "root" rather than document body.
+ // returns:
+ // the closest parent that matches the query, including the current
+ // node in this dojo/NodeList if it matches the query.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".red").closest(".container");
+ // returns the div with class "container".
+ return this._getRelatedUniqueNodes(null, function(node, ary){
+ do{
+ if(dquery._filterResult([node], query, root).length){
+ return node;
+ }
+ }while(node != root && (node = node.parentNode) && node.nodeType == 1);
+ return null; //To make rhino strict checking happy.
+ }); // dojo/NodeList
+ },
+
+ parent: function(/*String?*/ query){
+ // summary:
+ // Returns immediate parent elements for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the parent elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // immediate parent elements for nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".text").parent();
+ // returns the two divs with class "blue".
+ // Running this code:
+ // | dojo.query(".text").parent(".first");
+ // returns the one div with class "blue" and "first".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ return node.parentNode;
+ }); // dojo/NodeList
+ },
+
+ parents: function(/*String?*/ query){
+ // summary:
+ // Returns all parent elements for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the child elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // all parent elements for nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".text").parents();
+ // returns the two divs with class "blue", the div with class "container",
+ // | the body element and the html element.
+ // Running this code:
+ // | dojo.query(".text").parents(".container");
+ // returns the one div with class "container".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var pary = [];
+ while(node.parentNode){
+ node = node.parentNode;
+ pary.push(node);
+ }
+ return pary;
+ }); // dojo/NodeList
+ },
+
+ siblings: function(/*String?*/ query){
+ // summary:
+ // Returns all sibling elements for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the sibling elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // all sibling elements for nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".first").siblings();
+ // returns the two divs with class "red" and the other div
+ // | with class "blue" that does not have "first".
+ // Running this code:
+ // | dojo.query(".first").siblings(".red");
+ // returns the two div with class "red".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var pary = [];
+ var nodes = (node.parentNode && node.parentNode.childNodes);
+ for(var i = 0; i < nodes.length; i++){
+ if(nodes[i] != node){
+ pary.push(nodes[i]);
+ }
+ }
+ return pary;
+ }); // dojo/NodeList
+ },
+
+ next: function(/*String?*/ query){
+ // summary:
+ // Returns the next element for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the next elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // the next element for nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".first").next();
+ // returns the div with class "red" and has innerHTML of "Red Two".
+ // Running this code:
+ // | dojo.query(".last").next(".red");
+ // does not return any elements.
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var next = node.nextSibling;
+ while(next && next.nodeType != 1){
+ next = next.nextSibling;
+ }
+ return next;
+ }); // dojo/NodeList
+ },
+
+ nextAll: function(/*String?*/ query){
+ // summary:
+ // Returns all sibling elements that come after the nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the sibling elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // all sibling elements that come after the nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".first").nextAll();
+ // returns the two divs with class of "next".
+ // Running this code:
+ // | dojo.query(".first").nextAll(".red");
+ // returns the one div with class "red" and innerHTML "Red Two".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var pary = [];
+ var next = node;
+ while((next = next.nextSibling)){
+ if(next.nodeType == 1){
+ pary.push(next);
+ }
+ }
+ return pary;
+ }); // dojo/NodeList
+ },
+
+ prev: function(/*String?*/ query){
+ // summary:
+ // Returns the previous element for nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the previous elements.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // the previous element for nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".first").prev();
+ // returns the div with class "red" and has innerHTML of "Red One".
+ // Running this code:
+ // | dojo.query(".first").prev(".blue");
+ // does not return any elements.
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var prev = node.previousSibling;
+ while(prev && prev.nodeType != 1){
+ prev = prev.previousSibling;
+ }
+ return prev;
+ }); // dojo/NodeList
+ },
+
+ prevAll: function(/*String?*/ query){
+ // summary:
+ // Returns all sibling elements that come before the nodes in this dojo/NodeList.
+ // Optionally takes a query to filter the sibling elements.
+ // description:
+ // The returned nodes will be in reverse DOM order -- the first node in the list will
+ // be the node closest to the original node/NodeList.
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // query:
+ // a CSS selector.
+ // returns:
+ // all sibling elements that come before the nodes in this dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".second").prevAll();
+ // returns the two divs with class of "prev".
+ // Running this code:
+ // | dojo.query(".first").prevAll(".red");
+ // returns the one div with class "red prev" and innerHTML "Red One".
+ return this._getRelatedUniqueNodes(query, function(node, ary){
+ var pary = [];
+ var prev = node;
+ while((prev = prev.previousSibling)){
+ if(prev.nodeType == 1){
+ pary.push(prev);
+ }
+ }
+ return pary;
+ }); // dojo/NodeList
+ },
+
+ andSelf: function(){
+ // summary:
+ // Adds the nodes from the previous dojo/NodeList to the current dojo/NodeList.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // | Some Text
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".second").prevAll().andSelf();
+ // returns the two divs with class of "prev", as well as the div with class "second".
+ return this.concat(this._parent); // dojo/NodeList
+ },
+
+ //Alternate methods for the :first/:last/:even/:odd pseudos.
+ first: function(){
+ // summary:
+ // Returns the first node in this dojo/NodeList as a dojo/NodeList.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // returns:
+ // the first node in this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".blue").first();
+ // returns the div with class "blue" and "first".
+ return this._wrap(((this[0] && [this[0]]) || []), this); // dojo/NodeList
+ },
+
+ last: function(){
+ // summary:
+ // Returns the last node in this dojo/NodeList as a dojo/NodeList.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // returns:
+ // the last node in this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".blue").last();
+ // returns the last div with class "blue",
+ return this._wrap((this.length ? [this[this.length - 1]] : []), this); // dojo/NodeList
+ },
+
+ even: function(){
+ // summary:
+ // Returns the even nodes in this dojo/NodeList as a dojo/NodeList.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // returns:
+ // the even nodes in this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".interior").even();
+ // returns the two divs with class "blue"
+ return this.filter(function(item, i){
+ return i % 2 != 0;
+ }); // dojo/NodeList
+ },
+
+ odd: function(){
+ // summary:
+ // Returns the odd nodes in this dojo/NodeList as a dojo/NodeList.
+ // description:
+ // .end() can be used on the returned dojo/NodeList to get back to the
+ // original dojo/NodeList.
+ // returns:
+ // the odd nodes in this dojo/NodeList
+ // example:
+ // assume a DOM created by this markup:
+ // |
+ // |
Red One
+ // |
Blue One
+ // |
Red Two
+ // |
Blue Two
+ // |
+ // Running this code:
+ // | dojo.query(".interior").odd();
+ // returns the two divs with class "red"
+ return this.filter(function(item, i){
+ return i % 2 == 0;
+ }); // dojo/NodeList
+ }
+});
+
+return NodeList;
+});
diff --git a/src/main/resources/static/dojo/NodeList.js b/src/main/resources/static/dojo/NodeList.js
new file mode 100644
index 0000000000000000000000000000000000000000..1f6d2563237055803a3248ed3829dd7dfcd3c193
--- /dev/null
+++ b/src/main/resources/static/dojo/NodeList.js
@@ -0,0 +1,6 @@
+define(["./query"], function(query){
+ // This class is just for documentation purposes, so NodeList shows up well in the API viewer,
+ // and to simplify writing API doc for all the methods that take NodeList as a parameter, or return a NodeList.
+ return query.NodeList;
+});
+
diff --git a/src/main/resources/static/dojo/OpenAjax.js b/src/main/resources/static/dojo/OpenAjax.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b3c8918b6bed929f41118cfeea20bf12757cd78
--- /dev/null
+++ b/src/main/resources/static/dojo/OpenAjax.js
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * OpenAjax.js
+ *
+ * Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance.
+ * Specification is under development at:
+ *
+ * http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
+ *
+ * Copyright 2006-2007 OpenAjax Alliance
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless
+ * required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ ******************************************************************************/
+
+// prevent re-definition of the OpenAjax object
+if(!window["OpenAjax"]){
+ OpenAjax = new function(){
+ // summary:
+ // the OpenAjax hub
+ // description:
+ // see http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
+
+ var libs = {};
+ var ooh = "org.openajax.hub.";
+
+ var h = {};
+ this.hub = h;
+ h.implementer = "http://openajax.org";
+ h.implVersion = "0.6";
+ h.specVersion = "0.6";
+ h.implExtraData = {};
+ h.libraries = libs;
+
+ h.registerLibrary = function(prefix, nsURL, version, extra){
+ libs[prefix] = {
+ prefix: prefix,
+ namespaceURI: nsURL,
+ version: version,
+ extraData: extra
+ };
+ this.publish(ooh+"registerLibrary", libs[prefix]);
+ };
+ h.unregisterLibrary = function(prefix){
+ this.publish(ooh+"unregisterLibrary", libs[prefix]);
+ delete libs[prefix];
+ };
+
+ h._subscriptions = { c:{}, s:[] };
+ h._cleanup = [];
+ h._subIndex = 0;
+ h._pubDepth = 0;
+
+ h.subscribe = function(name, callback, scope, subscriberData, filter){
+ if(!scope){
+ scope = window;
+ }
+ var handle = name + "." + this._subIndex;
+ var sub = { scope: scope, cb: callback, fcb: filter, data: subscriberData, sid: this._subIndex++, hdl: handle };
+ var path = name.split(".");
+ this._subscribe(this._subscriptions, path, 0, sub);
+ return handle;
+ };
+
+ h.publish = function(name, message){
+ var path = name.split(".");
+ this._pubDepth++;
+ this._publish(this._subscriptions, path, 0, name, message);
+ this._pubDepth--;
+ if((this._cleanup.length > 0) && (this._pubDepth == 0)){
+ for(var i = 0; i < this._cleanup.length; i++){
+ this.unsubscribe(this._cleanup[i].hdl);
+ }
+ delete(this._cleanup);
+ this._cleanup = [];
+ }
+ };
+
+ h.unsubscribe = function(sub){
+ var path = sub.split(".");
+ var sid = path.pop();
+ this._unsubscribe(this._subscriptions, path, 0, sid);
+ };
+
+ h._subscribe = function(tree, path, index, sub){
+ var token = path[index];
+ if(index == path.length){
+ tree.s.push(sub);
+ }else{
+ if(typeof tree.c == "undefined"){
+ tree.c = {};
+ }
+ if(typeof tree.c[token] == "undefined"){
+ tree.c[token] = { c: {}, s: [] };
+ }
+ this._subscribe(tree.c[token], path, index + 1, sub);
+ }
+ };
+
+ h._publish = function(tree, path, index, name, msg){
+ if(typeof tree != "undefined"){
+ var node;
+ if(index == path.length){
+ node = tree;
+ }else{
+ this._publish(tree.c[path[index]], path, index + 1, name, msg);
+ this._publish(tree.c["*"], path, index + 1, name, msg);
+ node = tree.c["**"];
+ }
+ if(typeof node != "undefined"){
+ var callbacks = node.s;
+ var max = callbacks.length;
+ for(var i = 0; i < max; i++){
+ if(callbacks[i].cb){
+ var sc = callbacks[i].scope;
+ var cb = callbacks[i].cb;
+ var fcb = callbacks[i].fcb;
+ var d = callbacks[i].data;
+ if(typeof cb == "string"){
+ // get a function object
+ cb = sc[cb];
+ }
+ if(typeof fcb == "string"){
+ // get a function object
+ fcb = sc[fcb];
+ }
+ if((!fcb) ||
+ (fcb.call(sc, name, msg, d))){
+ cb.call(sc, name, msg, d);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ h._unsubscribe = function(tree, path, index, sid){
+ if(typeof tree != "undefined"){
+ if(index < path.length){
+ var childNode = tree.c[path[index]];
+ this._unsubscribe(childNode, path, index + 1, sid);
+ if(childNode.s.length == 0){
+ for(var x in childNode.c)
+ return;
+ delete tree.c[path[index]];
+ }
+ return;
+ }
+ else{
+ var callbacks = tree.s;
+ var max = callbacks.length;
+ for(var i = 0; i < max; i++){
+ if(sid == callbacks[i].sid){
+ if(this._pubDepth > 0){
+ callbacks[i].cb = null;
+ this._cleanup.push(callbacks[i]);
+ }
+ else
+ callbacks.splice(i, 1);
+ return;
+ }
+ }
+ }
+ }
+ };
+
+ // The following function is provided for automatic testing purposes.
+ // It is not expected to be deployed in run-time OpenAjax Hub implementations.
+ h.reinit = function(){
+ for (var lib in OpenAjax.hub.libraries){
+ delete OpenAjax.hub.libraries[lib];
+ }
+ OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "0.6", {});
+
+ delete OpenAjax._subscriptions;
+ OpenAjax._subscriptions = {c:{},s:[]};
+ delete OpenAjax._cleanup;
+ OpenAjax._cleanup = [];
+ OpenAjax._subIndex = 0;
+ OpenAjax._pubDepth = 0;
+ };
+ };
+
+ // Register the OpenAjax Hub itself as a library.
+ OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "0.6", {});
+
+}
diff --git a/src/main/resources/static/dojo/Stateful.js b/src/main/resources/static/dojo/Stateful.js
new file mode 100644
index 0000000000000000000000000000000000000000..b839a8672ba451a8b49cac5699f2e64b84539272
--- /dev/null
+++ b/src/main/resources/static/dojo/Stateful.js
@@ -0,0 +1,213 @@
+define(["./_base/declare", "./_base/lang", "./_base/array", "./when"], function(declare, lang, array, when){
+ // module:
+ // dojo/Stateful
+
+return declare("dojo.Stateful", null, {
+ // summary:
+ // Base class for objects that provide named properties with optional getter/setter
+ // control and the ability to watch for property changes
+ //
+ // The class also provides the functionality to auto-magically manage getters
+ // and setters for object attributes/properties.
+ //
+ // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
+ // the xxx is a name of the attribute to handle. So an attribute of "foo"
+ // would have a custom getter of _fooGetter and a custom setter of _fooSetter.
+ //
+ // example:
+ // | var obj = new dojo.Stateful();
+ // | obj.watch("foo", function(){
+ // | console.log("foo changed to " + this.get("foo"));
+ // | });
+ // | obj.set("foo","bar");
+
+ // _attrPairNames: Hash
+ // Used across all instances a hash to cache attribute names and their getter
+ // and setter names.
+ _attrPairNames: {},
+
+ _getAttrNames: function(name){
+ // summary:
+ // Helper function for get() and set().
+ // Caches attribute name values so we don't do the string ops every time.
+ // tags:
+ // private
+
+ var apn = this._attrPairNames;
+ if(apn[name]){ return apn[name]; }
+ return (apn[name] = {
+ s: "_" + name + "Setter",
+ g: "_" + name + "Getter"
+ });
+ },
+
+ postscript: function(/*Object?*/ params){
+ // Automatic setting of params during construction
+ if (params){ this.set(params); }
+ },
+
+ _get: function(name, names){
+ // summary:
+ // Private function that does a get based off a hash of names
+ // names:
+ // Hash of names of custom attributes
+ return typeof this[names.g] === "function" ? this[names.g]() : this[name];
+ },
+ get: function(/*String*/name){
+ // summary:
+ // Get a property on a Stateful instance.
+ // name:
+ // The property to get.
+ // returns:
+ // The property value on this Stateful instance.
+ // description:
+ // Get a named property on a Stateful object. The property may
+ // potentially be retrieved via a getter method in subclasses. In the base class
+ // this just retrieves the object's property.
+ // For example:
+ // | stateful = new dojo.Stateful({foo: 3});
+ // | stateful.get("foo") // returns 3
+ // | stateful.foo // returns 3
+
+ return this._get(name, this._getAttrNames(name)); //Any
+ },
+ set: function(/*String*/name, /*Object*/value){
+ // summary:
+ // Set a property on a Stateful instance
+ // name:
+ // The property to set.
+ // value:
+ // The value to set in the property.
+ // returns:
+ // The function returns this dojo.Stateful instance.
+ // description:
+ // Sets named properties on a stateful object and notifies any watchers of
+ // the property. A programmatic setter may be defined in subclasses.
+ // For example:
+ // | stateful = new dojo.Stateful();
+ // | stateful.watch(function(name, oldValue, value){
+ // | // this will be called on the set below
+ // | }
+ // | stateful.set(foo, 5);
+ //
+ // set() may also be called with a hash of name/value pairs, ex:
+ // | myObj.set({
+ // | foo: "Howdy",
+ // | bar: 3
+ // | })
+ // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
+
+ // If an object is used, iterate through object
+ if(typeof name === "object"){
+ for(var x in name){
+ if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
+ this.set(x, name[x]);
+ }
+ }
+ return this;
+ }
+
+ var names = this._getAttrNames(name),
+ oldValue = this._get(name, names),
+ setter = this[names.s],
+ result;
+ if(typeof setter === "function"){
+ // use the explicit setter
+ result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
+ }else{
+ // no setter so set attribute directly
+ this[name] = value;
+ }
+ if(this._watchCallbacks){
+ var self = this;
+ // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
+ when(result, function(){
+ self._watchCallbacks(name, oldValue, value);
+ });
+ }
+ return this; // dojo/Stateful
+ },
+ _changeAttrValue: function(name, value){
+ // summary:
+ // Internal helper for directly changing an attribute value.
+ //
+ // name: String
+ // The property to set.
+ // value: Mixed
+ // The value to set in the property.
+ //
+ // description:
+ // Directly change the value of an attribute on an object, bypassing any
+ // accessor setter. Also handles the calling of watch and emitting events.
+ // It is designed to be used by descendent class when there are two values
+ // of attributes that are linked, but calling .set() is not appropriate.
+
+ var oldValue = this.get(name);
+ this[name] = value;
+ if(this._watchCallbacks){
+ this._watchCallbacks(name, oldValue, value);
+ }
+ return this; // dojo/Stateful
+ },
+ watch: function(/*String?*/name, /*Function*/callback){
+ // summary:
+ // Watches a property for changes
+ // name:
+ // Indicates the property to watch. This is optional (the callback may be the
+ // only parameter), and if omitted, all the properties will be watched
+ // returns:
+ // An object handle for the watch. The unwatch method of this object
+ // can be used to discontinue watching this property:
+ // | var watchHandle = obj.watch("foo", callback);
+ // | watchHandle.unwatch(); // callback won't be called now
+ // callback:
+ // The function to execute when the property changes. This will be called after
+ // the property has been changed. The callback will be called with the |this|
+ // set to the instance, the first argument as the name of the property, the
+ // second argument as the old value and the third argument as the new value.
+
+ var callbacks = this._watchCallbacks;
+ if(!callbacks){
+ var self = this;
+ callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
+ var notify = function(propertyCallbacks){
+ if(propertyCallbacks){
+ propertyCallbacks = propertyCallbacks.slice();
+ for(var i = 0, l = propertyCallbacks.length; i < l; i++){
+ propertyCallbacks[i].call(self, name, oldValue, value);
+ }
+ }
+ };
+ notify(callbacks['_' + name]);
+ if(!ignoreCatchall){
+ notify(callbacks["*"]); // the catch-all
+ }
+ }; // we use a function instead of an object so it will be ignored by JSON conversion
+ }
+ if(!callback && typeof name === "function"){
+ callback = name;
+ name = "*";
+ }else{
+ // prepend with dash to prevent name conflicts with function (like "name" property)
+ name = '_' + name;
+ }
+ var propertyCallbacks = callbacks[name];
+ if(typeof propertyCallbacks !== "object"){
+ propertyCallbacks = callbacks[name] = [];
+ }
+ propertyCallbacks.push(callback);
+
+ // TODO: Remove unwatch in 2.0
+ var handle = {};
+ handle.unwatch = handle.remove = function(){
+ var index = array.indexOf(propertyCallbacks, callback);
+ if(index > -1){
+ propertyCallbacks.splice(index, 1);
+ }
+ };
+ return handle; //Object
+ }
+
+});
+
+});
diff --git a/src/main/resources/static/dojo/_base/Color.js b/src/main/resources/static/dojo/_base/Color.js
new file mode 100644
index 0000000000000000000000000000000000000000..6f416e5f755fde9b995641d559cb31b43763dca9
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/Color.js
@@ -0,0 +1,214 @@
+define(["./kernel", "./lang", "./array", "./config"], function(dojo, lang, ArrayUtil, config){
+
+ var Color = dojo.Color = function(/*Array|String|Object*/ color){
+ // summary:
+ // Takes a named string, hex string, array of rgb or rgba values,
+ // an object with r, g, b, and a properties, or another `Color` object
+ // and creates a new Color instance to work from.
+ //
+ // example:
+ // Work with a Color instance:
+ // | var c = new Color();
+ // | c.setColor([0,0,0]); // black
+ // | var hex = c.toHex(); // #000000
+ //
+ // example:
+ // Work with a node's color:
+ // | var color = dojo.style("someNode", "backgroundColor");
+ // | var n = new Color(color);
+ // | // adjust the color some
+ // | n.r *= .5;
+ // | console.log(n.toString()); // rgb(128, 255, 255);
+ if(color){ this.setColor(color); }
+ };
+
+ // FIXME:
+ // there's got to be a more space-efficient way to encode or discover
+ // these!! Use hex?
+ Color.named = {
+ // summary:
+ // Dictionary list of all CSS named colors, by name. Values are 3-item arrays with corresponding RG and B values.
+ "black": [0,0,0],
+ "silver": [192,192,192],
+ "gray": [128,128,128],
+ "white": [255,255,255],
+ "maroon": [128,0,0],
+ "red": [255,0,0],
+ "purple": [128,0,128],
+ "fuchsia":[255,0,255],
+ "green": [0,128,0],
+ "lime": [0,255,0],
+ "olive": [128,128,0],
+ "yellow": [255,255,0],
+ "navy": [0,0,128],
+ "blue": [0,0,255],
+ "teal": [0,128,128],
+ "aqua": [0,255,255],
+ "transparent": config.transparentColor || [0,0,0,0]
+ };
+
+ lang.extend(Color, {
+ r: 255, g: 255, b: 255, a: 1,
+ _set: function(r, g, b, a){
+ var t = this; t.r = r; t.g = g; t.b = b; t.a = a;
+ },
+ setColor: function(/*Array|String|Object*/ color){
+ // summary:
+ // Takes a named string, hex string, array of rgb or rgba values,
+ // an object with r, g, b, and a properties, or another `Color` object
+ // and sets this color instance to that value.
+ //
+ // example:
+ // | var c = new Color(); // no color
+ // | c.setColor("#ededed"); // greyish
+ if(lang.isString(color)){
+ Color.fromString(color, this);
+ }else if(lang.isArray(color)){
+ Color.fromArray(color, this);
+ }else{
+ this._set(color.r, color.g, color.b, color.a);
+ if(!(color instanceof Color)){ this.sanitize(); }
+ }
+ return this; // Color
+ },
+ sanitize: function(){
+ // summary:
+ // Ensures the object has correct attributes
+ // description:
+ // the default implementation does nothing, include dojo.colors to
+ // augment it with real checks
+ return this; // Color
+ },
+ toRgb: function(){
+ // summary:
+ // Returns 3 component array of rgb values
+ // example:
+ // | var c = new Color("#000000");
+ // | console.log(c.toRgb()); // [0,0,0]
+ var t = this;
+ return [t.r, t.g, t.b]; // Array
+ },
+ toRgba: function(){
+ // summary:
+ // Returns a 4 component array of rgba values from the color
+ // represented by this object.
+ var t = this;
+ return [t.r, t.g, t.b, t.a]; // Array
+ },
+ toHex: function(){
+ // summary:
+ // Returns a CSS color string in hexadecimal representation
+ // example:
+ // | console.log(new Color([0,0,0]).toHex()); // #000000
+ var arr = ArrayUtil.map(["r", "g", "b"], function(x){
+ var s = this[x].toString(16);
+ return s.length < 2 ? "0" + s : s;
+ }, this);
+ return "#" + arr.join(""); // String
+ },
+ toCss: function(/*Boolean?*/ includeAlpha){
+ // summary:
+ // Returns a css color string in rgb(a) representation
+ // example:
+ // | var c = new Color("#FFF").toCss();
+ // | console.log(c); // rgb('255','255','255')
+ var t = this, rgb = t.r + ", " + t.g + ", " + t.b;
+ return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")"; // String
+ },
+ toString: function(){
+ // summary:
+ // Returns a visual representation of the color
+ return this.toCss(true); // String
+ }
+ });
+
+ Color.blendColors = dojo.blendColors = function(
+ /*Color*/ start,
+ /*Color*/ end,
+ /*Number*/ weight,
+ /*Color?*/ obj
+ ){
+ // summary:
+ // Blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend,
+ // can reuse a previously allocated Color object for the result
+ var t = obj || new Color();
+ ArrayUtil.forEach(["r", "g", "b", "a"], function(x){
+ t[x] = start[x] + (end[x] - start[x]) * weight;
+ if(x != "a"){ t[x] = Math.round(t[x]); }
+ });
+ return t.sanitize(); // Color
+ };
+
+ Color.fromRgb = dojo.colorFromRgb = function(/*String*/ color, /*Color?*/ obj){
+ // summary:
+ // Returns a `Color` instance from a string of the form
+ // "rgb(...)" or "rgba(...)". Optionally accepts a `Color`
+ // object to update with the parsed value and return instead of
+ // creating a new object.
+ // returns:
+ // A Color object. If obj is passed, it will be the return value.
+ var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);
+ return m && Color.fromArray(m[1].split(/\s*,\s*/), obj); // Color
+ };
+
+ Color.fromHex = dojo.colorFromHex = function(/*String*/ color, /*Color?*/ obj){
+ // summary:
+ // Converts a hex string with a '#' prefix to a color object.
+ // Supports 12-bit #rgb shorthand. Optionally accepts a
+ // `Color` object to update with the parsed value.
+ //
+ // returns:
+ // A Color object. If obj is passed, it will be the return value.
+ //
+ // example:
+ // | var thing = dojo.colorFromHex("#ededed"); // grey, longhand
+ //
+ // example:
+ // | var thing = dojo.colorFromHex("#000"); // black, shorthand
+ var t = obj || new Color(),
+ bits = (color.length == 4) ? 4 : 8,
+ mask = (1 << bits) - 1;
+ color = Number("0x" + color.substr(1));
+ if(isNaN(color)){
+ return null; // Color
+ }
+ ArrayUtil.forEach(["b", "g", "r"], function(x){
+ var c = color & mask;
+ color >>= bits;
+ t[x] = bits == 4 ? 17 * c : c;
+ });
+ t.a = 1;
+ return t; // Color
+ };
+
+ Color.fromArray = dojo.colorFromArray = function(/*Array*/ a, /*Color?*/ obj){
+ // summary:
+ // Builds a `Color` from a 3 or 4 element array, mapping each
+ // element in sequence to the rgb(a) values of the color.
+ // example:
+ // | var myColor = dojo.colorFromArray([237,237,237,0.5]); // grey, 50% alpha
+ // returns:
+ // A Color object. If obj is passed, it will be the return value.
+ var t = obj || new Color();
+ t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3]));
+ if(isNaN(t.a)){ t.a = 1; }
+ return t.sanitize(); // Color
+ };
+
+ Color.fromString = dojo.colorFromString = function(/*String*/ str, /*Color?*/ obj){
+ // summary:
+ // Parses `str` for a color value. Accepts hex, rgb, and rgba
+ // style color values.
+ // description:
+ // Acceptable input values for str may include arrays of any form
+ // accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or
+ // rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10,
+ // 10, 50)"
+ // returns:
+ // A Color object. If obj is passed, it will be the return value.
+ var a = Color.named[str];
+ return a && Color.fromArray(a, obj) || Color.fromRgb(str, obj) || Color.fromHex(str, obj); // Color
+ };
+
+ return Color;
+});
diff --git a/src/main/resources/static/dojo/_base/Deferred.js b/src/main/resources/static/dojo/_base/Deferred.js
new file mode 100644
index 0000000000000000000000000000000000000000..ed857eeb3b75a87dcb73b1d5420aab16f62b6e04
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/Deferred.js
@@ -0,0 +1,383 @@
+define([
+ "./kernel",
+ "../Deferred",
+ "../promise/Promise",
+ "../errors/CancelError",
+ "../has",
+ "./lang",
+ "../when"
+], function(dojo, NewDeferred, Promise, CancelError, has, lang, when){
+ // module:
+ // dojo/_base/Deferred
+
+ var mutator = function(){};
+ var freeze = Object.freeze || function(){};
+ // A deferred provides an API for creating and resolving a promise.
+ var Deferred = dojo.Deferred = function(/*Function?*/ canceller){
+ // summary:
+ // Deprecated. This module defines the legacy dojo/_base/Deferred API.
+ // New code should use dojo/Deferred instead.
+ // description:
+ // The Deferred API is based on the concept of promises that provide a
+ // generic interface into the eventual completion of an asynchronous action.
+ // The motivation for promises fundamentally is about creating a
+ // separation of concerns that allows one to achieve the same type of
+ // call patterns and logical data flow in asynchronous code as can be
+ // achieved in synchronous code. Promises allows one
+ // to be able to call a function purely with arguments needed for
+ // execution, without conflating the call with concerns of whether it is
+ // sync or async. One shouldn't need to alter a call's arguments if the
+ // implementation switches from sync to async (or vice versa). By having
+ // async functions return promises, the concerns of making the call are
+ // separated from the concerns of asynchronous interaction (which are
+ // handled by the promise).
+ //
+ // The Deferred is a type of promise that provides methods for fulfilling the
+ // promise with a successful result or an error. The most important method for
+ // working with Dojo's promises is the then() method, which follows the
+ // CommonJS proposed promise API. An example of using a Dojo promise:
+ //
+ // | var resultingPromise = someAsyncOperation.then(function(result){
+ // | ... handle result ...
+ // | },
+ // | function(error){
+ // | ... handle error ...
+ // | });
+ //
+ // The .then() call returns a new promise that represents the result of the
+ // execution of the callback. The callbacks will never affect the original promises value.
+ //
+ // The Deferred instances also provide the following functions for backwards compatibility:
+ //
+ // - addCallback(handler)
+ // - addErrback(handler)
+ // - callback(result)
+ // - errback(result)
+ //
+ // Callbacks are allowed to return promises themselves, so
+ // you can build complicated sequences of events with ease.
+ //
+ // The creator of the Deferred may specify a canceller. The canceller
+ // is a function that will be called if Deferred.cancel is called
+ // before the Deferred fires. You can use this to implement clean
+ // aborting of an XMLHttpRequest, etc. Note that cancel will fire the
+ // deferred with a CancelledError (unless your canceller returns
+ // another kind of error), so the errbacks should be prepared to
+ // handle that error for cancellable Deferreds.
+ // example:
+ // | var deferred = new Deferred();
+ // | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
+ // | return deferred;
+ // example:
+ // Deferred objects are often used when making code asynchronous. It
+ // may be easiest to write functions in a synchronous manner and then
+ // split code using a deferred to trigger a response to a long-lived
+ // operation. For example, instead of register a callback function to
+ // denote when a rendering operation completes, the function can
+ // simply return a deferred:
+ //
+ // | // callback style:
+ // | function renderLotsOfData(data, callback){
+ // | var success = false
+ // | try{
+ // | for(var x in data){
+ // | renderDataitem(data[x]);
+ // | }
+ // | success = true;
+ // | }catch(e){ }
+ // | if(callback){
+ // | callback(success);
+ // | }
+ // | }
+ //
+ // | // using callback style
+ // | renderLotsOfData(someDataObj, function(success){
+ // | // handles success or failure
+ // | if(!success){
+ // | promptUserToRecover();
+ // | }
+ // | });
+ // | // NOTE: no way to add another callback here!!
+ // example:
+ // Using a Deferred doesn't simplify the sending code any, but it
+ // provides a standard interface for callers and senders alike,
+ // providing both with a simple way to service multiple callbacks for
+ // an operation and freeing both sides from worrying about details
+ // such as "did this get called already?". With Deferreds, new
+ // callbacks can be added at any time.
+ //
+ // | // Deferred style:
+ // | function renderLotsOfData(data){
+ // | var d = new Deferred();
+ // | try{
+ // | for(var x in data){
+ // | renderDataitem(data[x]);
+ // | }
+ // | d.callback(true);
+ // | }catch(e){
+ // | d.errback(new Error("rendering failed"));
+ // | }
+ // | return d;
+ // | }
+ //
+ // | // using Deferred style
+ // | renderLotsOfData(someDataObj).then(null, function(){
+ // | promptUserToRecover();
+ // | });
+ // | // NOTE: addErrback and addCallback both return the Deferred
+ // | // again, so we could chain adding callbacks or save the
+ // | // deferred for later should we need to be notified again.
+ // example:
+ // In this example, renderLotsOfData is synchronous and so both
+ // versions are pretty artificial. Putting the data display on a
+ // timeout helps show why Deferreds rock:
+ //
+ // | // Deferred style and async func
+ // | function renderLotsOfData(data){
+ // | var d = new Deferred();
+ // | setTimeout(function(){
+ // | try{
+ // | for(var x in data){
+ // | renderDataitem(data[x]);
+ // | }
+ // | d.callback(true);
+ // | }catch(e){
+ // | d.errback(new Error("rendering failed"));
+ // | }
+ // | }, 100);
+ // | return d;
+ // | }
+ //
+ // | // using Deferred style
+ // | renderLotsOfData(someDataObj).then(null, function(){
+ // | promptUserToRecover();
+ // | });
+ //
+ // Note that the caller doesn't have to change his code at all to
+ // handle the asynchronous case.
+
+ var result, finished, canceled, fired, isError, head, nextListener;
+ var promise = (this.promise = new Promise());
+
+ function complete(value){
+ if(finished){
+ throw new Error("This deferred has already been resolved");
+ }
+ result = value;
+ finished = true;
+ notify();
+ }
+ function notify(){
+ var mutated;
+ while(!mutated && nextListener){
+ var listener = nextListener;
+ nextListener = nextListener.next;
+ if((mutated = (listener.progress == mutator))){ // assignment and check
+ finished = false;
+ }
+
+ var func = (isError ? listener.error : listener.resolved);
+ if(has("config-useDeferredInstrumentation")){
+ if(isError && NewDeferred.instrumentRejected){
+ NewDeferred.instrumentRejected(result, !!func);
+ }
+ }
+ if(func){
+ try{
+ var newResult = func(result);
+ if (newResult && typeof newResult.then === "function"){
+ newResult.then(lang.hitch(listener.deferred, "resolve"), lang.hitch(listener.deferred, "reject"), lang.hitch(listener.deferred, "progress"));
+ continue;
+ }
+ var unchanged = mutated && newResult === undefined;
+ if(mutated && !unchanged){
+ isError = newResult instanceof Error;
+ }
+ listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult);
+ }catch(e){
+ listener.deferred.reject(e);
+ }
+ }else{
+ if(isError){
+ listener.deferred.reject(result);
+ }else{
+ listener.deferred.resolve(result);
+ }
+ }
+ }
+ }
+
+ this.isResolved = promise.isResolved = function(){
+ // summary:
+ // Checks whether the deferred has been resolved.
+ // returns: Boolean
+
+ return fired == 0;
+ };
+
+ this.isRejected = promise.isRejected = function(){
+ // summary:
+ // Checks whether the deferred has been rejected.
+ // returns: Boolean
+
+ return fired == 1;
+ };
+
+ this.isFulfilled = promise.isFulfilled = function(){
+ // summary:
+ // Checks whether the deferred has been resolved or rejected.
+ // returns: Boolean
+
+ return fired >= 0;
+ };
+
+ this.isCanceled = promise.isCanceled = function(){
+ // summary:
+ // Checks whether the deferred has been canceled.
+ // returns: Boolean
+
+ return canceled;
+ };
+
+ // calling resolve will resolve the promise
+ this.resolve = this.callback = function(value){
+ // summary:
+ // Fulfills the Deferred instance successfully with the provide value
+ this.fired = fired = 0;
+ this.results = [value, null];
+ complete(value);
+ };
+
+
+ // calling error will indicate that the promise failed
+ this.reject = this.errback = function(error){
+ // summary:
+ // Fulfills the Deferred instance as an error with the provided error
+ isError = true;
+ this.fired = fired = 1;
+ if(has("config-useDeferredInstrumentation")){
+ if(NewDeferred.instrumentRejected){
+ NewDeferred.instrumentRejected(error, !!nextListener);
+ }
+ }
+ complete(error);
+ this.results = [null, error];
+ };
+ // call progress to provide updates on the progress on the completion of the promise
+ this.progress = function(update){
+ // summary:
+ // Send progress events to all listeners
+ var listener = nextListener;
+ while(listener){
+ var progress = listener.progress;
+ progress && progress(update);
+ listener = listener.next;
+ }
+ };
+ this.addCallbacks = function(callback, errback){
+ // summary:
+ // Adds callback and error callback for this deferred instance.
+ // callback: Function?
+ // The callback attached to this deferred object.
+ // errback: Function?
+ // The error callback attached to this deferred object.
+ // returns:
+ // Returns this deferred object.
+ this.then(callback, errback, mutator);
+ return this; // Deferred
+ };
+ // provide the implementation of the promise
+ promise.then = this.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){
+ // summary:
+ // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for
+ // completion of a promise. The fulfilledHandler is called when the promise
+ // is fulfilled. The errorHandler is called when a promise fails. The
+ // progressHandler is called for progress events. All arguments are optional
+ // and non-function values are ignored. The progressHandler is not only an
+ // optional argument, but progress events are purely optional. Promise
+ // providers are not required to ever create progress events.
+ //
+ // This function will return a new promise that is fulfilled when the given
+ // fulfilledHandler or errorHandler callback is finished. This allows promise
+ // operations to be chained together. The value returned from the callback
+ // handler is the fulfillment value for the returned promise. If the callback
+ // throws an error, the returned promise will be moved to failed state.
+ //
+ // returns:
+ // Returns a new promise that represents the result of the
+ // execution of the callback. The callbacks will never affect the original promises value.
+ // example:
+ // An example of using a CommonJS compliant promise:
+ // | asyncComputeTheAnswerToEverything().
+ // | then(addTwo).
+ // | then(printResult, onError);
+ // | >44
+ //
+ var returnDeferred = progressCallback == mutator ? this : new Deferred(promise.cancel);
+ var listener = {
+ resolved: resolvedCallback,
+ error: errorCallback,
+ progress: progressCallback,
+ deferred: returnDeferred
+ };
+ if(nextListener){
+ head = head.next = listener;
+ }
+ else{
+ nextListener = head = listener;
+ }
+ if(finished){
+ notify();
+ }
+ return returnDeferred.promise; // Promise
+ };
+ var deferred = this;
+ promise.cancel = this.cancel = function(){
+ // summary:
+ // Cancels the asynchronous operation
+ if(!finished){
+ var error = canceller && canceller(deferred);
+ if(!finished){
+ if (!(error instanceof Error)){
+ error = new CancelError(error);
+ }
+ error.log = false;
+ deferred.reject(error);
+ }
+ }
+ canceled = true;
+ };
+ freeze(promise);
+ };
+ lang.extend(Deferred, {
+ addCallback: function(/*Function*/ callback){
+ // summary:
+ // Adds successful callback for this deferred instance.
+ // returns:
+ // Returns this deferred object.
+ return this.addCallbacks(lang.hitch.apply(dojo, arguments)); // Deferred
+ },
+
+ addErrback: function(/*Function*/ errback){
+ // summary:
+ // Adds error callback for this deferred instance.
+ // returns:
+ // Returns this deferred object.
+ return this.addCallbacks(null, lang.hitch.apply(dojo, arguments)); // Deferred
+ },
+
+ addBoth: function(/*Function*/ callback){
+ // summary:
+ // Add handler as both successful callback and error callback for this deferred instance.
+ // returns:
+ // Returns this deferred object.
+ var enclosed = lang.hitch.apply(dojo, arguments);
+ return this.addCallbacks(enclosed, enclosed); // Deferred
+ },
+ fired: -1
+ });
+
+ Deferred.when = dojo.when = when;
+
+ return Deferred;
+});
diff --git a/src/main/resources/static/dojo/_base/NodeList.js b/src/main/resources/static/dojo/_base/NodeList.js
new file mode 100644
index 0000000000000000000000000000000000000000..10a173cbe2d7afa65aff497f184359c2b3747bd9
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/NodeList.js
@@ -0,0 +1,110 @@
+define(["./kernel", "../query", "./array", "./html", "../NodeList-dom"], function(dojo, query, array){
+ // module:
+ // dojo/_base/NodeList
+
+ /*=====
+ return {
+ // summary:
+ // This module extends dojo/NodeList with the legacy connect(), coords(),
+ // blur(), focus(), change(), click(), error(), keydown(), keypress(),
+ // keyup(), load(), mousedown(), mouseenter(), mouseleave(), mousemove(),
+ // mouseout(), mouseover(), mouseup(), and submit() methods.
+ };
+ =====*/
+
+ var NodeList = query.NodeList,
+ nlp = NodeList.prototype;
+
+ nlp.connect = NodeList._adaptAsForEach(function(){
+ // don't bind early to dojo.connect since we no longer explicitly depend on it
+ return dojo.connect.apply(this, arguments);
+ });
+ /*=====
+ nlp.connect = function(methodName, objOrFunc, funcName){
+ // summary:
+ // Attach event handlers to every item of the NodeList. Uses dojo.connect()
+ // so event properties are normalized.
+ //
+ // Application must manually require() "dojo/_base/connect" before using this method.
+ // methodName: String
+ // the name of the method to attach to. For DOM events, this should be
+ // the lower-case name of the event
+ // objOrFunc: Object|Function|String
+ // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should
+ // reference a function or be the name of the function in the global
+ // namespace to attach. If 3 arguments are provided
+ // (methodName, objOrFunc, funcName), objOrFunc must be the scope to
+ // locate the bound function in
+ // funcName: String?
+ // optional. A string naming the function in objOrFunc to bind to the
+ // event. May also be a function reference.
+ // example:
+ // add an onclick handler to every button on the page
+ // | query("div:nth-child(odd)").connect("onclick", function(e){
+ // | console.log("clicked!");
+ // | });
+ // example:
+ // attach foo.bar() to every odd div's onmouseover
+ // | query("div:nth-child(odd)").connect("onmouseover", foo, "bar");
+
+ return null; // NodeList
+ };
+ =====*/
+
+ nlp.coords = NodeList._adaptAsMap(dojo.coords);
+ /*=====
+ nlp.coords = function(){
+ // summary:
+ // Deprecated: Use position() for border-box x/y/w/h
+ // or marginBox() for margin-box w/h/l/t.
+ // Returns the box objects of all elements in a node list as
+ // an Array (*not* a NodeList). Acts like `domGeom.coords`, though assumes
+ // the node passed is each node in this list.
+
+ return []; // Array
+ };
+ =====*/
+
+ NodeList.events = [
+ // summary:
+ // list of all DOM events used in NodeList
+ "blur", "focus", "change", "click", "error", "keydown", "keypress",
+ "keyup", "load", "mousedown", "mouseenter", "mouseleave", "mousemove",
+ "mouseout", "mouseover", "mouseup", "submit"
+ ];
+
+ // FIXME: pseudo-doc the above automatically generated on-event functions
+
+ // syntactic sugar for DOM events
+ array.forEach(NodeList.events, function(evt){
+ var _oe = "on" + evt;
+ nlp[_oe] = function(a, b){
+ return this.connect(_oe, a, b);
+ };
+ // FIXME: should these events trigger publishes?
+ /*
+ return (a ? this.connect(_oe, a, b) :
+ this.forEach(function(n){
+ // FIXME:
+ // listeners get buried by
+ // addEventListener and can't be dug back
+ // out to be triggered externally.
+ // see:
+ // http://developer.mozilla.org/en/docs/DOM:element
+
+ console.log(n, evt, _oe);
+
+ // FIXME: need synthetic event support!
+ var _e = { target: n, faux: true, type: evt };
+ // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });
+ try{ n[evt](_e); }catch(e){ console.log(e); }
+ try{ n[_oe](_e); }catch(e){ console.log(e); }
+ })
+ );
+ */
+ }
+ );
+
+ dojo.NodeList = NodeList;
+ return NodeList;
+});
diff --git a/src/main/resources/static/dojo/_base/array.js b/src/main/resources/static/dojo/_base/array.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd9fc71760f9672f1e6cd1dc4b490a9d63210312
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/array.js
@@ -0,0 +1,350 @@
+define(["./kernel", "../has", "./lang"], function(dojo, has, lang){
+ // module:
+ // dojo/_base/array
+
+ // our old simple function builder stuff
+ var cache = {}, u;
+
+ function buildFn(fn){
+ return cache[fn] = new Function("item", "index", "array", fn); // Function
+ }
+ // magic snippet: if(typeof fn == "string") fn = cache[fn] || buildFn(fn);
+
+ // every & some
+
+ function everyOrSome(some){
+ var every = !some;
+ return function(a, fn, o){
+ var i = 0, l = a && a.length || 0, result;
+ if(l && typeof a == "string") a = a.split("");
+ if(typeof fn == "string") fn = cache[fn] || buildFn(fn);
+ if(o){
+ for(; i < l; ++i){
+ result = !fn.call(o, a[i], i, a);
+ if(some ^ result){
+ return !result;
+ }
+ }
+ }else{
+ for(; i < l; ++i){
+ result = !fn(a[i], i, a);
+ if(some ^ result){
+ return !result;
+ }
+ }
+ }
+ return every; // Boolean
+ };
+ }
+
+ // indexOf, lastIndexOf
+
+ function index(up){
+ var delta = 1, lOver = 0, uOver = 0;
+ if(!up){
+ delta = lOver = uOver = -1;
+ }
+ return function(a, x, from, last){
+ if(last && delta > 0){
+ // TODO: why do we use a non-standard signature? why do we need "last"?
+ return array.lastIndexOf(a, x, from);
+ }
+ var l = a && a.length || 0, end = up ? l + uOver : lOver, i;
+ if(from === u){
+ i = up ? lOver : l + uOver;
+ }else{
+ if(from < 0){
+ i = l + from;
+ if(i < 0){
+ i = lOver;
+ }
+ }else{
+ i = from >= l ? l + uOver : from;
+ }
+ }
+ if(l && typeof a == "string") a = a.split("");
+ for(; i != end; i += delta){
+ if(a[i] == x){
+ return i; // Number
+ }
+ }
+ return -1; // Number
+ };
+ }
+
+ var array = {
+ // summary:
+ // The Javascript v1.6 array extensions.
+
+ every: everyOrSome(false),
+ /*=====
+ every: function(arr, callback, thisObject){
+ // summary:
+ // Determines whether or not every item in arr satisfies the
+ // condition implemented by callback.
+ // arr: Array|String
+ // the array to iterate on. If a string, operates on individual characters.
+ // callback: Function|String
+ // a function is invoked with three arguments: item, index,
+ // and array and returns true if the condition is met.
+ // thisObject: Object?
+ // may be used to scope the call to callback
+ // returns: Boolean
+ // description:
+ // This function corresponds to the JavaScript 1.6 Array.every() method, with one difference: when
+ // run over sparse arrays, this implementation passes the "holes" in the sparse array to
+ // the callback function with a value of undefined. JavaScript 1.6's every skips the holes in the sparse array.
+ // For more details, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/every
+ // example:
+ // | // returns false
+ // | array.every([1, 2, 3, 4], function(item){ return item>1; });
+ // example:
+ // | // returns true
+ // | array.every([1, 2, 3, 4], function(item){ return item>0; });
+ },
+ =====*/
+
+ some: everyOrSome(true),
+ /*=====
+ some: function(arr, callback, thisObject){
+ // summary:
+ // Determines whether or not any item in arr satisfies the
+ // condition implemented by callback.
+ // arr: Array|String
+ // the array to iterate over. If a string, operates on individual characters.
+ // callback: Function|String
+ // a function is invoked with three arguments: item, index,
+ // and array and returns true if the condition is met.
+ // thisObject: Object?
+ // may be used to scope the call to callback
+ // returns: Boolean
+ // description:
+ // This function corresponds to the JavaScript 1.6 Array.some() method, with one difference: when
+ // run over sparse arrays, this implementation passes the "holes" in the sparse array to
+ // the callback function with a value of undefined. JavaScript 1.6's some skips the holes in the sparse array.
+ // For more details, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/some
+ // example:
+ // | // is true
+ // | array.some([1, 2, 3, 4], function(item){ return item>1; });
+ // example:
+ // | // is false
+ // | array.some([1, 2, 3, 4], function(item){ return item<1; });
+ },
+ =====*/
+
+ indexOf: index(true),
+ /*=====
+ indexOf: function(arr, value, fromIndex, findLast){
+ // summary:
+ // locates the first index of the provided value in the
+ // passed array. If the value is not found, -1 is returned.
+ // description:
+ // This method corresponds to the JavaScript 1.6 Array.indexOf method, with two differences:
+ //
+ // 1. when run over sparse arrays, the Dojo function invokes the callback for every index
+ // whereas JavaScript 1.6's indexOf skips the holes in the sparse array.
+ // 2. uses equality (==) rather than strict equality (===)
+ //
+ // For details on this method, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/indexOf
+ // arr: Array
+ // value: Object
+ // fromIndex: Integer?
+ // findLast: Boolean?
+ // Makes indexOf() work like lastIndexOf(). Used internally; not meant for external usage.
+ // returns: Number
+ },
+ =====*/
+
+ lastIndexOf: index(false),
+ /*=====
+ lastIndexOf: function(arr, value, fromIndex){
+ // summary:
+ // locates the last index of the provided value in the passed
+ // array. If the value is not found, -1 is returned.
+ // description:
+ // This method corresponds to the JavaScript 1.6 Array.lastIndexOf method, with two differences:
+ //
+ // 1. when run over sparse arrays, the Dojo function invokes the callback for every index
+ // whereas JavaScript 1.6's lasIndexOf skips the holes in the sparse array.
+ // 2. uses equality (==) rather than strict equality (===)
+ //
+ // For details on this method, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/lastIndexOf
+ // arr: Array,
+ // value: Object,
+ // fromIndex: Integer?
+ // returns: Number
+ },
+ =====*/
+
+ forEach: function(arr, callback, thisObject){
+ // summary:
+ // for every item in arr, callback is invoked. Return values are ignored.
+ // If you want to break out of the loop, consider using array.every() or array.some().
+ // forEach does not allow breaking out of the loop over the items in arr.
+ // arr:
+ // the array to iterate over. If a string, operates on individual characters.
+ // callback:
+ // a function is invoked with three arguments: item, index, and array
+ // thisObject:
+ // may be used to scope the call to callback
+ // description:
+ // This function corresponds to the JavaScript 1.6 Array.forEach() method, with one difference: when
+ // run over sparse arrays, this implementation passes the "holes" in the sparse array to
+ // the callback function with a value of undefined. JavaScript 1.6's forEach skips the holes in the sparse array.
+ // For more details, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach
+ // example:
+ // | // log out all members of the array:
+ // | array.forEach(
+ // | [ "thinger", "blah", "howdy", 10 ],
+ // | function(item){
+ // | console.log(item);
+ // | }
+ // | );
+ // example:
+ // | // log out the members and their indexes
+ // | array.forEach(
+ // | [ "thinger", "blah", "howdy", 10 ],
+ // | function(item, idx, arr){
+ // | console.log(item, "at index:", idx);
+ // | }
+ // | );
+ // example:
+ // | // use a scoped object member as the callback
+ // |
+ // | var obj = {
+ // | prefix: "logged via obj.callback:",
+ // | callback: function(item){
+ // | console.log(this.prefix, item);
+ // | }
+ // | };
+ // |
+ // | // specifying the scope function executes the callback in that scope
+ // | array.forEach(
+ // | [ "thinger", "blah", "howdy", 10 ],
+ // | obj.callback,
+ // | obj
+ // | );
+ // |
+ // | // alternately, we can accomplish the same thing with lang.hitch()
+ // | array.forEach(
+ // | [ "thinger", "blah", "howdy", 10 ],
+ // | lang.hitch(obj, "callback")
+ // | );
+ // arr: Array|String
+ // callback: Function|String
+ // thisObject: Object?
+
+ var i = 0, l = arr && arr.length || 0;
+ if(l && typeof arr == "string") arr = arr.split("");
+ if(typeof callback == "string") callback = cache[callback] || buildFn(callback);
+ if(thisObject){
+ for(; i < l; ++i){
+ callback.call(thisObject, arr[i], i, arr);
+ }
+ }else{
+ for(; i < l; ++i){
+ callback(arr[i], i, arr);
+ }
+ }
+ },
+
+ map: function(arr, callback, thisObject, Ctr){
+ // summary:
+ // applies callback to each element of arr and returns
+ // an Array with the results
+ // arr: Array|String
+ // the array to iterate on. If a string, operates on
+ // individual characters.
+ // callback: Function|String
+ // a function is invoked with three arguments, (item, index,
+ // array), and returns a value
+ // thisObject: Object?
+ // may be used to scope the call to callback
+ // returns: Array
+ // description:
+ // This function corresponds to the JavaScript 1.6 Array.map() method, with one difference: when
+ // run over sparse arrays, this implementation passes the "holes" in the sparse array to
+ // the callback function with a value of undefined. JavaScript 1.6's map skips the holes in the sparse array.
+ // For more details, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
+ // example:
+ // | // returns [2, 3, 4, 5]
+ // | array.map([1, 2, 3, 4], function(item){ return item+1 });
+
+ // TODO: why do we have a non-standard signature here? do we need "Ctr"?
+ var i = 0, l = arr && arr.length || 0, out = new (Ctr || Array)(l);
+ if(l && typeof arr == "string") arr = arr.split("");
+ if(typeof callback == "string") callback = cache[callback] || buildFn(callback);
+ if(thisObject){
+ for(; i < l; ++i){
+ out[i] = callback.call(thisObject, arr[i], i, arr);
+ }
+ }else{
+ for(; i < l; ++i){
+ out[i] = callback(arr[i], i, arr);
+ }
+ }
+ return out; // Array
+ },
+
+ filter: function(arr, callback, thisObject){
+ // summary:
+ // Returns a new Array with those items from arr that match the
+ // condition implemented by callback.
+ // arr: Array
+ // the array to iterate over.
+ // callback: Function|String
+ // a function that is invoked with three arguments (item,
+ // index, array). The return of this function is expected to
+ // be a boolean which determines whether the passed-in item
+ // will be included in the returned array.
+ // thisObject: Object?
+ // may be used to scope the call to callback
+ // returns: Array
+ // description:
+ // This function corresponds to the JavaScript 1.6 Array.filter() method, with one difference: when
+ // run over sparse arrays, this implementation passes the "holes" in the sparse array to
+ // the callback function with a value of undefined. JavaScript 1.6's filter skips the holes in the sparse array.
+ // For more details, see:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
+ // example:
+ // | // returns [2, 3, 4]
+ // | array.filter([1, 2, 3, 4], function(item){ return item>1; });
+
+ // TODO: do we need "Ctr" here like in map()?
+ var i = 0, l = arr && arr.length || 0, out = [], value;
+ if(l && typeof arr == "string") arr = arr.split("");
+ if(typeof callback == "string") callback = cache[callback] || buildFn(callback);
+ if(thisObject){
+ for(; i < l; ++i){
+ value = arr[i];
+ if(callback.call(thisObject, value, i, arr)){
+ out.push(value);
+ }
+ }
+ }else{
+ for(; i < l; ++i){
+ value = arr[i];
+ if(callback(value, i, arr)){
+ out.push(value);
+ }
+ }
+ }
+ return out; // Array
+ },
+
+ clearCache: function(){
+ cache = {};
+ }
+ };
+
+
+ has("extend-dojo") && lang.mixin(dojo, array);
+
+ return array;
+});
diff --git a/src/main/resources/static/dojo/_base/browser.js b/src/main/resources/static/dojo/_base/browser.js
new file mode 100644
index 0000000000000000000000000000000000000000..68ab3633826170a87a9161597ad7a57c051a9095
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/browser.js
@@ -0,0 +1,28 @@
+if(require.has){
+ require.has.add("config-selectorEngine", "acme");
+}
+define([
+ "../ready",
+ "./kernel",
+ "./connect", // until we decide if connect is going back into non-browser environments
+ "./unload",
+ "./window",
+ "./event",
+ "./html",
+ "./NodeList",
+ "../query",
+ "./xhr",
+ "./fx"], function(dojo){
+
+ // module:
+ // dojo/_base/browser
+
+ /*=====
+ return {
+ // summary:
+ // This module causes the browser-only base modules to be loaded.
+ };
+ =====*/
+
+ return dojo;
+});
diff --git a/src/main/resources/static/dojo/_base/config.js b/src/main/resources/static/dojo/_base/config.js
new file mode 100644
index 0000000000000000000000000000000000000000..d94d9c8077e665c5989dd7a1df948b065937b61c
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/config.js
@@ -0,0 +1,193 @@
+define(["../has", "require"], function(has, require){
+ // module:
+ // dojo/_base/config
+
+/*=====
+return {
+ // summary:
+ // This module defines the user configuration during bootstrap.
+ // description:
+ // By defining user configuration as a module value, an entire configuration can be specified in a build,
+ // thereby eliminating the need for sniffing and or explicitly setting in the global variable dojoConfig.
+ // Also, when multiple instances of dojo exist in a single application, each will necessarily be located
+ // at an unique absolute module identifier as given by the package configuration. Implementing configuration
+ // as a module allows for specifying unique, per-instance configurations.
+ // example:
+ // Create a second instance of dojo with a different, instance-unique configuration (assume the loader and
+ // dojo.js are already loaded).
+ // | // specify a configuration that creates a new instance of dojo at the absolute module identifier "myDojo"
+ // | require({
+ // | packages:[{
+ // | name:"myDojo",
+ // | location:".", //assume baseUrl points to dojo.js
+ // | }]
+ // | });
+ // |
+ // | // specify a configuration for the myDojo instance
+ // | define("myDojo/config", {
+ // | // normal configuration variables go here, e.g.,
+ // | locale:"fr-ca"
+ // | });
+ // |
+ // | // load and use the new instance of dojo
+ // | require(["myDojo"], function(dojo){
+ // | // dojo is the new instance of dojo
+ // | // use as required
+ // | });
+
+ // isDebug: Boolean
+ // Defaults to `false`. If set to `true`, ensures that Dojo provides
+ // extended debugging feedback via Firebug. If Firebug is not available
+ // on your platform, setting `isDebug` to `true` will force Dojo to
+ // pull in (and display) the version of Firebug Lite which is
+ // integrated into the Dojo distribution, thereby always providing a
+ // debugging/logging console when `isDebug` is enabled. Note that
+ // Firebug's `console.*` methods are ALWAYS defined by Dojo. If
+ // `isDebug` is false and you are on a platform without Firebug, these
+ // methods will be defined as no-ops.
+ isDebug: false,
+
+ // locale: String
+ // The locale to assume for loading localized resources in this page,
+ // specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
+ // Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`.
+ // See the documentation for `dojo.i18n` and `dojo.requireLocalization`
+ // for details on loading localized resources. If no locale is specified,
+ // Dojo assumes the locale of the user agent, according to `navigator.userLanguage`
+ // or `navigator.language` properties.
+ locale: undefined,
+
+ // extraLocale: Array
+ // No default value. Specifies additional locales whose
+ // resources should also be loaded alongside the default locale when
+ // calls to `dojo.requireLocalization()` are processed.
+ extraLocale: undefined,
+
+ // baseUrl: String
+ // The directory in which `dojo.js` is located. Under normal
+ // conditions, Dojo auto-detects the correct location from which it
+ // was loaded. You may need to manually configure `baseUrl` in cases
+ // where you have renamed `dojo.js` or in which `` tags confuse
+ // some browsers (e.g. IE 6). The variable `dojo.baseUrl` is assigned
+ // either the value of `djConfig.baseUrl` if one is provided or the
+ // auto-detected root if not. Other modules are located relative to
+ // this path. The path should end in a slash.
+ baseUrl: undefined,
+
+ // modulePaths: [deprecated] Object
+ // A map of module names to paths relative to `dojo.baseUrl`. The
+ // key/value pairs correspond directly to the arguments which
+ // `dojo.registerModulePath` accepts. Specifying
+ // `djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent
+ // of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple
+ // modules may be configured via `djConfig.modulePaths`.
+ modulePaths: {},
+
+ // addOnLoad: Function|Array
+ // Adds a callback via dojo/ready. Useful when Dojo is added after
+ // the page loads and djConfig.afterOnLoad is true. Supports the same
+ // arguments as dojo/ready. When using a function reference, use
+ // `djConfig.addOnLoad = function(){};`. For object with function name use
+ // `djConfig.addOnLoad = [myObject, "functionName"];` and for object with
+ // function reference use
+ // `djConfig.addOnLoad = [myObject, function(){}];`
+ addOnLoad: null,
+
+ // parseOnLoad: Boolean
+ // Run the parser after the page is loaded
+ parseOnLoad: false,
+
+ // require: String[]
+ // An array of module names to be loaded immediately after dojo.js has been included
+ // in a page.
+ require: [],
+
+ // defaultDuration: Number
+ // Default duration, in milliseconds, for wipe and fade animations within dijits.
+ // Assigned to dijit.defaultDuration.
+ defaultDuration: 200,
+
+ // dojoBlankHtmlUrl: String
+ // Used by some modules to configure an empty iframe. Used by dojo/io/iframe and
+ // dojo/back, and dijit/popup support in IE where an iframe is needed to make sure native
+ // controls do not bleed through the popups. Normally this configuration variable
+ // does not need to be set, except when using cross-domain/CDN Dojo builds.
+ // Save dojo/resources/blank.html to your domain and set `djConfig.dojoBlankHtmlUrl`
+ // to the path on your domain your copy of blank.html.
+ dojoBlankHtmlUrl: undefined,
+
+ // ioPublish: Boolean?
+ // Set this to true to enable publishing of topics for the different phases of
+ // IO operations. Publishing is done via dojo/topic.publish(). See dojo/main.__IoPublish for a list
+ // of topics that are published.
+ ioPublish: false,
+
+ // useCustomLogger: Anything?
+ // If set to a value that evaluates to true such as a string or array and
+ // isDebug is true and Firebug is not available or running, then it bypasses
+ // the creation of Firebug Lite allowing you to define your own console object.
+ useCustomLogger: undefined,
+
+ // transparentColor: Array
+ // Array containing the r, g, b components used as transparent color in dojo.Color;
+ // if undefined, [255,255,255] (white) will be used.
+ transparentColor: undefined,
+
+ // deps: Function|Array
+ // Defines dependencies to be used before the loader has been loaded.
+ // When provided, they cause the loader to execute require(deps, callback)
+ // once it has finished loading. Should be used with callback.
+ deps: undefined,
+
+ // callback: Function|Array
+ // Defines a callback to be used when dependencies are defined before
+ // the loader has been loaded. When provided, they cause the loader to
+ // execute require(deps, callback) once it has finished loading.
+ // Should be used with deps.
+ callback: undefined,
+
+ // deferredInstrumentation: Boolean
+ // Whether deferred instrumentation should be loaded or included
+ // in builds.
+ deferredInstrumentation: true,
+
+ // useDeferredInstrumentation: Boolean|String
+ // Whether the deferred instrumentation should be used.
+ //
+ // * `"report-rejections"`: report each rejection as it occurs.
+ // * `true` or `1` or `"report-unhandled-rejections"`: wait 1 second
+ // in an attempt to detect unhandled rejections.
+ useDeferredInstrumentation: "report-unhandled-rejections"
+};
+=====*/
+
+ var result = {};
+ if(has("dojo-config-api")){
+ // must be the dojo loader; take a shallow copy of require.rawConfig
+ var src = require.rawConfig, p;
+ for(p in src){
+ result[p] = src[p];
+ }
+ }else{
+ var adviseHas = function(featureSet, prefix, booting){
+ for(p in featureSet){
+ p!="has" && has.add(prefix + p, featureSet[p], 0, booting);
+ }
+ };
+ result = has("dojo-loader") ?
+ // must be a built version of the dojo loader; all config stuffed in require.rawConfig
+ require.rawConfig :
+ // a foreign loader
+ this.dojoConfig || this.djConfig || {};
+ adviseHas(result, "config", 1);
+ adviseHas(result.has, "", 1);
+ }
+
+ if(!result.locale && typeof navigator != "undefined"){
+ // Default locale for browsers.
+ result.locale = (navigator.language || navigator.userLanguage).toLowerCase();
+ }
+
+ return result;
+});
+
diff --git a/src/main/resources/static/dojo/_base/configFirefoxExtension.js b/src/main/resources/static/dojo/_base/configFirefoxExtension.js
new file mode 100644
index 0000000000000000000000000000000000000000..4115c61727d29ff766d91cb98312104e8b84b976
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/configFirefoxExtension.js
@@ -0,0 +1,336 @@
+// TODO: this file needs to be converted to the v1.7 loader
+
+// a host environment specifically built for Mozilla extensions, but derived
+// from the browser host environment
+if(typeof window != 'undefined'){
+ dojo.isBrowser = true;
+ dojo._name = "browser";
+
+
+ // FIXME: PORTME
+ // http://developer.mozilla.org/en/mozIJSSubScriptLoader
+
+
+ // attempt to figure out the path to dojo if it isn't set in the config
+ (function(){
+ // this is a scope protection closure. We set browser versions and grab
+ // the URL we were loaded from here.
+
+ // FIXME: need to probably use a different reference to "document" to get the hosting XUL environment
+
+ dojo.baseUrl = dojo.config.baseUrl;
+
+ // fill in the rendering support information in dojo.render.*
+ var n = navigator;
+ var dua = n.userAgent;
+ var dav = n.appVersion;
+ var tv = parseFloat(dav);
+
+ dojo.isMozilla = dojo.isMoz = tv;
+ if(dojo.isMoz){
+ dojo.isFF = parseFloat(dua.split("Firefox/")[1]) || undefined;
+ }
+
+ // FIXME
+ dojo.isQuirks = document.compatMode == "BackCompat";
+
+ // FIXME
+ // TODO: is the HTML LANG attribute relevant?
+ dojo.locale = dojo.config.locale || n.language.toLowerCase();
+
+ dojo._xhrObj = function(){
+ return new XMLHttpRequest();
+ };
+
+ // monkey-patch _loadUri to handle file://, chrome://, and resource:// url's
+ var oldLoadUri = dojo._loadUri;
+ dojo._loadUri = function(uri, cb){
+ var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){
+ return String(uri).indexOf(prefix) == 0;
+ });
+ if(handleLocal){
+ // see:
+ // http://developer.mozilla.org/en/mozIJSSubScriptLoader
+ var l = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Components.interfaces.mozIJSSubScriptLoader);
+ var value = l.loadSubScript(uri, dojo.global);
+ if(cb){ cb(value); }
+ return true;
+ }else{
+ // otherwise, call the pre-existing version
+ return oldLoadUri.apply(dojo, arguments);
+ }
+ };
+
+ // FIXME: PORTME
+ dojo._isDocumentOk = function(http){
+ var stat = http.status || 0;
+ return (stat >= 200 && stat < 300) || // Boolean
+ stat == 304 || // allow any 2XX response code
+ stat == 1223 || // get it out of the cache
+ (!stat && (location.protocol == "file:" || location.protocol == "chrome:") );
+ };
+
+ // FIXME: PORTME
+ // var owloc = window.location+"";
+ // var base = document.getElementsByTagName("base");
+ // var hasBase = (base && base.length > 0);
+ var hasBase = false;
+
+ dojo._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){
+ // summary:
+ // Read the contents of the specified uri and return those contents.
+ // uri:
+ // A relative or absolute uri. If absolute, it still must be in
+ // the same "domain" as we are.
+ // fail_ok:
+ // Default false. If fail_ok and loading fails, return null
+ // instead of throwing.
+ // returns:
+ // The response text. null is returned when there is a
+ // failure and failure is okay (an exception otherwise)
+
+ // alert("_getText: " + uri);
+
+ // NOTE: must be declared before scope switches ie. this._xhrObj()
+ var http = dojo._xhrObj();
+
+ if(!hasBase && dojo._Url){
+ uri = (new dojo._Url(uri)).toString();
+ }
+ if(dojo.config.cacheBust){
+ //Make sure we have a string before string methods are used on uri
+ uri += "";
+ uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(dojo.config.cacheBust).replace(/\W+/g, "");
+ }
+ var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){
+ return String(uri).indexOf(prefix) == 0;
+ });
+ if(handleLocal){
+ // see:
+ // http://forums.mozillazine.org/viewtopic.php?p=921150#921150
+ var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+ var scriptableStream = Components
+ .classes["@mozilla.org/scriptableinputstream;1"]
+ .getService(Components.interfaces.nsIScriptableInputStream);
+
+ var channel = ioService.newChannel(uri, null, null);
+ var input = channel.open();
+ scriptableStream.init(input);
+ var str = scriptableStream.read(input.available());
+ scriptableStream.close();
+ input.close();
+ return str;
+ }else{
+ http.open('GET', uri, false);
+ try{
+ http.send(null);
+ // alert(http);
+ if(!dojo._isDocumentOk(http)){
+ var err = Error("Unable to load " + uri + " status:" + http.status);
+ err.status = http.status;
+ err.responseText = http.responseText;
+ throw err;
+ }
+ }catch(e){
+ if(fail_ok){
+ return null;
+ } // null
+ // rethrow the exception
+ throw e;
+ }
+ return http.responseText; // String
+ }
+ };
+
+ dojo._windowUnloaders = [];
+
+ // FIXME: PORTME
+ dojo.windowUnloaded = function(){
+ // summary:
+ // signal fired by impending window destruction. You may use
+ // dojo.addOnWIndowUnload() or dojo.connect() to this method to perform
+ // page/application cleanup methods. See dojo.addOnWindowUnload for more info.
+ var mll = dojo._windowUnloaders;
+ while(mll.length){
+ (mll.pop())();
+ }
+ };
+
+ // FIXME: PORTME
+ dojo.addOnWindowUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
+ // summary:
+ // registers a function to be triggered when window.onunload fires.
+ // Be careful trying to modify the DOM or access JavaScript properties
+ // during this phase of page unloading: they may not always be available.
+ // Consider dojo.addOnUnload() if you need to modify the DOM or do heavy
+ // JavaScript work.
+ // example:
+ // | dojo.addOnWindowUnload(functionPointer)
+ // | dojo.addOnWindowUnload(object, "functionName")
+ // | dojo.addOnWindowUnload(object, function(){ /* ... */});
+
+ dojo._onto(dojo._windowUnloaders, obj, functionName);
+ };
+
+ // XUL specific APIs
+ var contexts = [];
+ var current = null;
+ dojo._defaultContext = [ window, document ];
+
+ dojo.pushContext = function(/*Object|String?*/g, /*MDocumentElement?*/d){
+ // summary:
+ // causes subsequent calls to Dojo methods to assume the
+ // passed object and, optionally, document as the default
+ // scopes to use. A 2-element array of the previous global and
+ // document are returned.
+ // description:
+ // dojo.pushContext treats contexts as a stack. The
+ // auto-detected contexts which are initially provided using
+ // dojo.setContext() require authors to keep state in order to
+ // "return" to a previous context, whereas the
+ // dojo.pushContext and dojo.popContext methods provide a more
+ // natural way to augment blocks of code to ensure that they
+ // execute in a different window or frame without issue. If
+ // called without any arguments, the default context (the
+ // context when Dojo is first loaded) is instead pushed into
+ // the stack. If only a single string is passed, a node in the
+ // intitial context's document is looked up and its
+ // contextWindow and contextDocument properties are used as
+ // the context to push. This means that iframes can be given
+ // an ID and code can be executed in the scope of the iframe's
+ // document in subsequent calls easily.
+ // g:
+ // The global context. If a string, the id of the frame to
+ // search for a context and document.
+ // d:
+ // The document element to execute subsequent code with.
+ var old = [dojo.global, dojo.doc];
+ contexts.push(old);
+ var n;
+ if(!g && !d){
+ n = dojo._defaultContext;
+ }else{
+ n = [ g, d ];
+ if(!d && dojo.isString(g)){
+ var t = document.getElementById(g);
+ if(t.contentDocument){
+ n = [t.contentWindow, t.contentDocument];
+ }
+ }
+ }
+ current = n;
+ dojo.setContext.apply(dojo, n);
+ return old; // Array
+ };
+
+ dojo.popContext = function(){
+ // summary:
+ // If the context stack contains elements, ensure that
+ // subsequent code executes in the *previous* context to the
+ // current context. The current context set ([global,
+ // document]) is returned.
+ var oc = current;
+ if(!contexts.length){
+ return oc;
+ }
+ dojo.setContext.apply(dojo, contexts.pop());
+ return oc;
+ };
+
+ // FIXME:
+ // don't really like the current arguments and order to
+ // _inContext, so don't make it public until it's right!
+ dojo._inContext = function(g, d, f){
+ var a = dojo._toArray(arguments);
+ f = a.pop();
+ if(a.length == 1){
+ d = null;
+ }
+ dojo.pushContext(g, d);
+ var r = f();
+ dojo.popContext();
+ return r;
+ };
+
+ })();
+
+ dojo._initFired = false;
+ // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
+ dojo._loadInit = function(e){
+ dojo._initFired = true;
+ // allow multiple calls, only first one will take effect
+ // A bug in khtml calls events callbacks for document for event which isnt supported
+ // for example a created contextmenu event calls DOMContentLoaded, workaround
+ var type = (e && e.type) ? e.type.toLowerCase() : "load";
+ if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; }
+ arguments.callee.initialized = true;
+ if(dojo._inFlightCount == 0){
+ dojo._modulesLoaded();
+ }
+ };
+
+ /*
+ (function(){
+ var _w = window;
+ var _handleNodeEvent = function(evtName, fp){
+ // summary:
+ // non-destructively adds the specified function to the node's
+ // evtName handler.
+ // evtName: should be in the form "onclick" for "onclick" handlers.
+ // Make sure you pass in the "on" part.
+ var oldHandler = _w[evtName] || function(){};
+ _w[evtName] = function(){
+ fp.apply(_w, arguments);
+ oldHandler.apply(_w, arguments);
+ };
+ };
+ // FIXME: PORT
+ // FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
+ _handleNodeEvent("onbeforeunload", function(){ dojo.unloaded(); });
+ _handleNodeEvent("onunload", function(){ dojo.windowUnloaded(); });
+ })();
+ */
+
+
+ // FIXME: PORTME
+ // this event fires a lot, namely for all plugin XUL overlays and for
+ // all iframes (in addition to window navigations). We only want
+ // Dojo's to fire once..but we might care if pages navigate. We'll
+ // probably need an extension-specific API
+ if(!dojo.config.afterOnLoad){
+ window.addEventListener("DOMContentLoaded", function(e){
+ dojo._loadInit(e);
+ // console.log("DOM content loaded", e);
+ }, false);
+ }
+
+} //if (typeof window != 'undefined')
+
+//Register any module paths set up in djConfig. Need to do this
+//in the hostenvs since hostenv_browser can read djConfig from a
+//script tag's attribute.
+(function(){
+ var mp = dojo.config["modulePaths"];
+ if(mp){
+ for(var param in mp){
+ dojo.registerModulePath(param, mp[param]);
+ }
+ }
+})();
+
+//Load debug code if necessary.
+if(dojo.config.isDebug){
+ // logging stub for extension logging
+ console.log = function(m){
+ var s = Components.classes["@mozilla.org/consoleservice;1"].getService(
+ Components.interfaces.nsIConsoleService
+ );
+ s.logStringMessage(m);
+ };
+ console.debug = function(){
+ console.log(dojo._toArray(arguments).join(" "));
+ };
+ // FIXME: what about the rest of the console.* methods? And is there any way to reach into firebug and log into it directly?
+}
diff --git a/src/main/resources/static/dojo/_base/configNode.js b/src/main/resources/static/dojo/_base/configNode.js
new file mode 100644
index 0000000000000000000000000000000000000000..1866795c1981ac8ae81d8f7f6d14625228d764e1
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/configNode.js
@@ -0,0 +1,87 @@
+exports.config = function(config){
+ // summary:
+ // This module provides bootstrap configuration for running dojo in node.js
+
+ // any command line arguments with the load flag are pushed into deps
+ for(var deps = [], args = [], i = 0; i < process.argv.length; i++){
+ var arg = (process.argv[i] + "").split("=");
+ if(arg[0] == "load"){
+ deps.push(arg[1]);
+ }else{
+ args.push(arg);
+ }
+ }
+
+ var fs = require("fs");
+
+ // make sure global require exists
+ //if (typeof global.require=="undefined"){
+ // global.require= {};
+ //}
+
+ // reset the has cache with node-appropriate values;
+ var hasCache = {
+ "host-node":1,
+ "host-browser":0,
+ "dom":0,
+ "dojo-has-api":1,
+ "dojo-xhr-factory":0,
+ "dojo-inject-api":1,
+ "dojo-timeout-api":0,
+ "dojo-trace-api":1,
+ "dojo-dom-ready-api":0,
+ "dojo-publish-privates":1,
+ "dojo-sniff":0,
+ "dojo-loader":1,
+ "dojo-test-xd":0,
+ "dojo-test-sniff":0
+ };
+ for(var p in hasCache){
+ config.hasCache[p] = hasCache[p];
+ }
+
+ var vm = require('vm'),
+ path = require('path');
+
+ // reset some configuration switches with node-appropriate values
+ var nodeConfig = {
+ baseUrl: path.dirname(process.argv[1]),
+ commandLineArgs:args,
+ deps:deps,
+ timeout:0,
+
+ // TODO: really get the locale
+ locale:"en-us",
+
+ loaderPatch: {
+ log:function(item){
+ // define debug for console messages during dev instead of console.log
+ // (node's heavy async makes console.log confusing sometimes)
+ var util = require("util");
+ util.debug(util.inspect(item));
+ },
+
+ eval: function(__text, __urlHint){
+ return vm.runInThisContext(__text, __urlHint);
+ },
+
+ injectUrl: function(url, callback){
+ try{
+ vm.runInThisContext(fs.readFileSync(url, "utf8"), url);
+ callback();
+ }catch(e){
+ this.log("failed to load resource (" + url + ")");
+ this.log(e);
+ }
+ },
+
+ getText: function(url, sync, onLoad){
+ // TODO: implement async and http/https handling
+ onLoad(fs.readFileSync(url, "utf8"));
+ }
+ }
+ };
+ for(p in nodeConfig){
+ config[p] = nodeConfig[p];
+ }
+};
diff --git a/src/main/resources/static/dojo/_base/configRhino.js b/src/main/resources/static/dojo/_base/configRhino.js
new file mode 100644
index 0000000000000000000000000000000000000000..2cbbf8887bf38d26c8f465155851692be117b076
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/configRhino.js
@@ -0,0 +1,121 @@
+function rhinoDojoConfig(config, baseUrl, rhinoArgs){
+ // summary:
+ // This module provides bootstrap configuration for running dojo in rhino.
+
+ // TODO: v1.6 tries to set dojo.doc and dojo.body in rhino; why?
+
+ // get a minimal console up
+ var log = function(hint, args){
+ print((hint ? hint + ":" : "") + args[0]);
+ for(var i = 1; i < args.length; i++){
+ print(", " + args[i]);
+ }
+ };
+ // intentionally define console in the global namespace
+ console= {
+ log: function(){ log(0, arguments); },
+ error: function(){ log("ERROR", arguments); },
+ warn: function(){ log("WARN", arguments); }
+ };
+
+ // any command line arguments with the load flag are pushed into deps
+ for(var deps = [], i = 0; i < rhinoArgs.length; i++){
+ var arg = (rhinoArgs[i] + "").split("=");
+ if(arg[0] == "load"){
+ deps.push(arg[1]);
+ }
+ }
+
+ // provides timed callbacks using Java threads
+ if(typeof setTimeout == "undefined" || typeof clearTimeout == "undefined"){
+ var timeouts = [];
+ clearTimeout = function(idx){
+ if(!timeouts[idx]){ return; }
+ timeouts[idx].stop();
+ };
+
+ setTimeout = function(func, delay){
+ var def = {
+ sleepTime:delay,
+ hasSlept:false,
+
+ run:function(){
+ if(!this.hasSlept){
+ this.hasSlept = true;
+ java.lang.Thread.currentThread().sleep(this.sleepTime);
+ }
+ try{
+ func();
+ }catch(e){
+ console.debug("Error running setTimeout thread:" + e);
+ }
+ }
+ };
+
+ var runnable = new java.lang.Runnable(def);
+ var thread = new java.lang.Thread(runnable);
+ thread.start();
+ return timeouts.push(thread) - 1;
+ };
+ }
+
+ var isLocal = function(url){
+ return (new java.io.File(url)).exists();
+ };
+
+ // reset the has cache with node-appropriate values;
+ var hasCache = {
+ "host-rhino":1,
+ "host-browser":0,
+ "dom":0,
+ "dojo-has-api":1,
+ "dojo-xhr-factory":0,
+ "dojo-inject-api":1,
+ "dojo-timeout-api":0,
+ "dojo-trace-api":1,
+ "dojo-loader-catches":1,
+ "dojo-dom-ready-api":0,
+ "dojo-publish-privates":1,
+ "dojo-sniff":0,
+ "dojo-loader":1,
+ "dojo-test-xd":0,
+ "dojo-test-sniff":0
+ };
+ for(var p in hasCache){
+ config.hasCache[p] = hasCache[p];
+ }
+
+ // reset some configuration switches with rhino-appropriate values
+ var rhinoConfig = {
+ baseUrl:baseUrl,
+ commandLineArgs:rhinoArgs,
+ deps:deps,
+ timeout:0,
+ locale:String(java.util.Locale.getDefault().toString().replace('_', '-').toLowerCase()),
+
+ loaderPatch:{
+ injectUrl: function(url, callback){
+ try{
+ if(isLocal(url)){
+ load(url);
+ }else{
+ require.eval(readUrl(url, "UTF-8"));
+ }
+ callback();
+ }catch(e){
+ console.log("failed to load resource (" + url + ")");
+ console.log(e);
+ }
+ },
+
+ getText: function(url, sync, onLoad){
+ // TODO: test https://bugzilla.mozilla.org/show_bug.cgi?id=471005; see v1.6 hostenv_rhino
+ // note: async mode not supported in rhino
+ onLoad(isLocal(url) ? readFile(url, "UTF-8") : readUrl(url, "UTF-8"));
+ }
+ }
+ };
+ for(p in rhinoConfig){
+ config[p] = rhinoConfig[p];
+ }
+}
diff --git a/src/main/resources/static/dojo/_base/configSpidermonkey.js b/src/main/resources/static/dojo/_base/configSpidermonkey.js
new file mode 100644
index 0000000000000000000000000000000000000000..af16fda3b4050e5faefe3b1b106b2b4c99c933cc
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/configSpidermonkey.js
@@ -0,0 +1,80 @@
+// TODO: this file needs to be converted to the v1.7 loader
+
+// module:
+// configSpidermonkey
+// summary:
+// SpiderMonkey host environment
+
+if(dojo.config["baseUrl"]){
+ dojo.baseUrl = dojo.config["baseUrl"];
+}else{
+ dojo.baseUrl = "./";
+}
+
+dojo._name = 'spidermonkey';
+
+
+
+dojo.isSpidermonkey = true;
+dojo.exit = function(exitcode){
+ quit(exitcode);
+};
+
+if(typeof print == "function"){
+ console.debug = print;
+}
+
+if(typeof line2pc == 'undefined'){
+ throw new Error("attempt to use SpiderMonkey host environment when no 'line2pc' global");
+}
+
+dojo._spidermonkeyCurrentFile = function(depth){
+ //
+ // This is a hack that determines the current script file by parsing a
+ // generated stack trace (relying on the non-standard "stack" member variable
+ // of the SpiderMonkey Error object).
+ //
+ // If param depth is passed in, it'll return the script file which is that far down
+ // the stack, but that does require that you know how deep your stack is when you are
+ // calling.
+ //
+ var s = '';
+ try{
+ throw Error("whatever");
+ }catch(e){
+ s = e.stack;
+ }
+ // lines are like: bu_getCurrentScriptURI_spidermonkey("ScriptLoader.js")@burst/Runtime.js:101
+ var matches = s.match(/[^@]*\.js/gi);
+ if(!matches){
+ throw Error("could not parse stack string: '" + s + "'");
+ }
+ var fname = (typeof depth != 'undefined' && depth) ? matches[depth + 1] : matches[matches.length - 1];
+ if(!fname){
+ throw Error("could not find file name in stack string '" + s + "'");
+ }
+ //print("SpiderMonkeyRuntime got fname '" + fname + "' from stack string '" + s + "'");
+ return fname;
+};
+
+// print(dojo._spidermonkeyCurrentFile(0));
+
+dojo._loadUri = function(uri){
+ // spidermonkey load() evaluates the contents into the global scope (which
+ // is what we want).
+ // TODO: sigh, load() does not return a useful value.
+ // Perhaps it is returning the value of the last thing evaluated?
+ // var ok =
+ load(uri);
+ // console.log("spidermonkey load(", uri, ") returned ", ok);
+ return 1;
+};
+
+//Register any module paths set up in djConfig. Need to do this
+//in the hostenvs since hostenv_browser can read djConfig from a
+//script tag's attribute.
+if(dojo.config["modulePaths"]){
+ for(var param in dojo.config["modulePaths"]){
+ dojo.registerModulePath(param, dojo.config["modulePaths"][param]);
+ }
+}
diff --git a/src/main/resources/static/dojo/_base/connect.js b/src/main/resources/static/dojo/_base/connect.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f08bf25ac6494d5db5bb4f9c2903c4c61b5a759
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/connect.js
@@ -0,0 +1,374 @@
+define(["./kernel", "../on", "../topic", "../aspect", "./event", "../mouse", "./sniff", "./lang", "../keys"], function(dojo, on, hub, aspect, eventModule, mouse, has, lang){
+// module:
+// dojo/_base/connect
+
+has.add("events-keypress-typed", function(){ // keypresses should only occur a printable character is hit
+ var testKeyEvent = {charCode: 0};
+ try{
+ testKeyEvent = document.createEvent("KeyboardEvent");
+ (testKeyEvent.initKeyboardEvent || testKeyEvent.initKeyEvent).call(testKeyEvent, "keypress", true, true, null, false, false, false, false, 9, 3);
+ }catch(e){}
+ return testKeyEvent.charCode == 0 && !has("opera");
+});
+
+function connect_(obj, event, context, method, dontFix){
+ method = lang.hitch(context, method);
+ if(!obj || !(obj.addEventListener || obj.attachEvent)){
+ // it is a not a DOM node and we are using the dojo.connect style of treating a
+ // method like an event, must go right to aspect
+ return aspect.after(obj || dojo.global, event, method, true);
+ }
+ if(typeof event == "string" && event.substring(0, 2) == "on"){
+ event = event.substring(2);
+ }
+ if(!obj){
+ obj = dojo.global;
+ }
+ if(!dontFix){
+ switch(event){
+ // dojo.connect has special handling for these event types
+ case "keypress":
+ event = keypress;
+ break;
+ case "mouseenter":
+ event = mouse.enter;
+ break;
+ case "mouseleave":
+ event = mouse.leave;
+ break;
+ }
+ }
+ return on(obj, event, method, dontFix);
+}
+
+var _punctMap = {
+ 106:42,
+ 111:47,
+ 186:59,
+ 187:43,
+ 188:44,
+ 189:45,
+ 190:46,
+ 191:47,
+ 192:96,
+ 219:91,
+ 220:92,
+ 221:93,
+ 222:39,
+ 229:113
+};
+var evtCopyKey = has("mac") ? "metaKey" : "ctrlKey";
+
+
+var _synthesizeEvent = function(evt, props){
+ var faux = lang.mixin({}, evt, props);
+ setKeyChar(faux);
+ // FIXME: would prefer to use lang.hitch: lang.hitch(evt, evt.preventDefault);
+ // but it throws an error when preventDefault is invoked on Safari
+ // does Event.preventDefault not support "apply" on Safari?
+ faux.preventDefault = function(){ evt.preventDefault(); };
+ faux.stopPropagation = function(){ evt.stopPropagation(); };
+ return faux;
+};
+function setKeyChar(evt){
+ evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
+ evt.charOrCode = evt.keyChar || evt.keyCode;
+}
+var keypress;
+if(has("events-keypress-typed")){
+ // this emulates Firefox's keypress behavior where every keydown can correspond to a keypress
+ var _trySetKeyCode = function(e, code){
+ try{
+ // squelch errors when keyCode is read-only
+ // (e.g. if keyCode is ctrl or shift)
+ return (e.keyCode = code);
+ }catch(e){
+ return 0;
+ }
+ };
+ keypress = function(object, listener){
+ var keydownSignal = on(object, "keydown", function(evt){
+ // munge key/charCode
+ var k=evt.keyCode;
+ // These are Windows Virtual Key Codes
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
+ var unprintable = (k!=13) && k!=32 && (k!=27||!has("ie")) && (k<48||k>90) && (k<96||k>111) && (k<186||k>192) && (k<219||k>222) && k!=229;
+ // synthesize keypress for most unprintables and CTRL-keys
+ if(unprintable||evt.ctrlKey){
+ var c = unprintable ? 0 : k;
+ if(evt.ctrlKey){
+ if(k==3 || k==13){
+ return listener.call(evt.currentTarget, evt); // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively
+ }else if(c>95 && c<106){
+ c -= 48; // map CTRL-[numpad 0-9] to ASCII
+ }else if((!evt.shiftKey)&&(c>=65&&c<=90)){
+ c += 32; // map CTRL-[A-Z] to lowercase
+ }else{
+ c = _punctMap[c] || c; // map other problematic CTRL combinations to ASCII
+ }
+ }
+ // simulate a keypress event
+ var faux = _synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
+ listener.call(evt.currentTarget, faux);
+ if(has("ie")){
+ _trySetKeyCode(evt, faux.keyCode);
+ }
+ }
+ });
+ var keypressSignal = on(object, "keypress", function(evt){
+ var c = evt.charCode;
+ c = c>=32 ? c : 0;
+ evt = _synthesizeEvent(evt, {charCode: c, faux: true});
+ return listener.call(this, evt);
+ });
+ return {
+ remove: function(){
+ keydownSignal.remove();
+ keypressSignal.remove();
+ }
+ };
+ };
+}else{
+ if(has("opera")){
+ keypress = function(object, listener){
+ return on(object, "keypress", function(evt){
+ var c = evt.which;
+ if(c==3){
+ c=99; // Mozilla maps CTRL-BREAK to CTRL-c
+ }
+ // can't trap some keys at all, like INSERT and DELETE
+ // there is no differentiating info between DELETE and ".", or INSERT and "-"
+ c = c<32 && !evt.shiftKey ? 0 : c;
+ if(evt.ctrlKey && !evt.shiftKey && c>=65 && c<=90){
+ // lowercase CTRL-[A-Z] keys
+ c += 32;
+ }
+ return listener.call(this, _synthesizeEvent(evt, { charCode: c }));
+ });
+ };
+ }else{
+ keypress = function(object, listener){
+ return on(object, "keypress", function(evt){
+ setKeyChar(evt);
+ return listener.call(this, evt);
+ });
+ };
+ }
+}
+
+var connect = {
+ // summary:
+ // This module defines the dojo.connect API.
+ // This modules also provides keyboard event handling helpers.
+ // This module exports an extension event for emulating Firefox's keypress handling.
+ // However, this extension event exists primarily for backwards compatibility and
+ // is not recommended. WebKit and IE uses an alternate keypress handling (only
+ // firing for printable characters, to distinguish from keydown events), and most
+ // consider the WebKit/IE behavior more desirable.
+
+ _keypress:keypress,
+
+ connect:function(obj, event, context, method, dontFix){
+ // summary:
+ // `dojo.connect` is a deprecated event handling and delegation method in
+ // Dojo. It allows one function to "listen in" on the execution of
+ // any other, triggering the second whenever the first is called. Many
+ // listeners may be attached to a function, and source functions may
+ // be either regular function calls or DOM events.
+ //
+ // description:
+ // Connects listeners to actions, so that after event fires, a
+ // listener is called with the same arguments passed to the original
+ // function.
+ //
+ // Since `dojo.connect` allows the source of events to be either a
+ // "regular" JavaScript function or a DOM event, it provides a uniform
+ // interface for listening to all the types of events that an
+ // application is likely to deal with though a single, unified
+ // interface. DOM programmers may want to think of it as
+ // "addEventListener for everything and anything".
+ //
+ // When setting up a connection, the `event` parameter must be a
+ // string that is the name of the method/event to be listened for. If
+ // `obj` is null, `kernel.global` is assumed, meaning that connections
+ // to global methods are supported but also that you may inadvertently
+ // connect to a global by passing an incorrect object name or invalid
+ // reference.
+ //
+ // `dojo.connect` generally is forgiving. If you pass the name of a
+ // function or method that does not yet exist on `obj`, connect will
+ // not fail, but will instead set up a stub method. Similarly, null
+ // arguments may simply be omitted such that fewer than 4 arguments
+ // may be required to set up a connection See the examples for details.
+ //
+ // The return value is a handle that is needed to
+ // remove this connection with `dojo.disconnect`.
+ //
+ // obj: Object?
+ // The source object for the event function.
+ // Defaults to `kernel.global` if null.
+ // If obj is a DOM node, the connection is delegated
+ // to the DOM event manager (unless dontFix is true).
+ //
+ // event: String
+ // String name of the event function in obj.
+ // I.e. identifies a property `obj[event]`.
+ //
+ // context: Object|null
+ // The object that method will receive as "this".
+ //
+ // If context is null and method is a function, then method
+ // inherits the context of event.
+ //
+ // If method is a string then context must be the source
+ // object object for method (context[method]). If context is null,
+ // kernel.global is used.
+ //
+ // method: String|Function
+ // A function reference, or name of a function in context.
+ // The function identified by method fires after event does.
+ // method receives the same arguments as the event.
+ // See context argument comments for information on method's scope.
+ //
+ // dontFix: Boolean?
+ // If obj is a DOM node, set dontFix to true to prevent delegation
+ // of this connection to the DOM event manager.
+ //
+ // example:
+ // When obj.onchange(), do ui.update():
+ // | dojo.connect(obj, "onchange", ui, "update");
+ // | dojo.connect(obj, "onchange", ui, ui.update); // same
+ //
+ // example:
+ // Using return value for disconnect:
+ // | var link = dojo.connect(obj, "onchange", ui, "update");
+ // | ...
+ // | dojo.disconnect(link);
+ //
+ // example:
+ // When onglobalevent executes, watcher.handler is invoked:
+ // | dojo.connect(null, "onglobalevent", watcher, "handler");
+ //
+ // example:
+ // When ob.onCustomEvent executes, customEventHandler is invoked:
+ // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
+ // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
+ //
+ // example:
+ // When ob.onCustomEvent executes, customEventHandler is invoked
+ // with the same scope (this):
+ // | dojo.connect(ob, "onCustomEvent", null, customEventHandler);
+ // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same
+ //
+ // example:
+ // When globalEvent executes, globalHandler is invoked
+ // with the same scope (this):
+ // | dojo.connect(null, "globalEvent", null, globalHandler);
+ // | dojo.connect("globalEvent", globalHandler); // same
+
+ // normalize arguments
+ var a=arguments, args=[], i=0;
+ // if a[0] is a String, obj was omitted
+ args.push(typeof a[0] == "string" ? null : a[i++], a[i++]);
+ // if the arg-after-next is a String or Function, context was NOT omitted
+ var a1 = a[i+1];
+ args.push(typeof a1 == "string" || typeof a1 == "function" ? a[i++] : null, a[i++]);
+ // absorb any additional arguments
+ for(var l=a.length; i= 0; --j){
+ proto = lin[j].prototype;
+ if(!proto.hasOwnProperty("declaredClass")){
+ proto.declaredClass = "uniqName_" + (counter++);
+ }
+ name = proto.declaredClass;
+ if(!nameMap.hasOwnProperty(name)){
+ nameMap[name] = {count: 0, refs: [], cls: lin[j]};
+ ++clsCount;
+ }
+ rec = nameMap[name];
+ if(top && top !== rec){
+ rec.refs.push(top);
+ ++top.count;
+ }
+ top = rec;
+ }
+ ++top.count;
+ roots[0].refs.push(top);
+ }
+
+ // remove classes without external references recursively
+ while(roots.length){
+ top = roots.pop();
+ result.push(top.cls);
+ --clsCount;
+ // optimization: follow a single-linked chain
+ while(refs = top.refs, refs.length == 1){
+ top = refs[0];
+ if(!top || --top.count){
+ // branch or end of chain => do not end to roots
+ top = 0;
+ break;
+ }
+ result.push(top.cls);
+ --clsCount;
+ }
+ if(top){
+ // branch
+ for(i = 0, l = refs.length; i < l; ++i){
+ top = refs[i];
+ if(!--top.count){
+ roots.push(top);
+ }
+ }
+ }
+ }
+ if(clsCount){
+ err("can't build consistent linearization", className);
+ }
+
+ // calculate the superclass offset
+ base = bases[0];
+ result[0] = base ?
+ base._meta && base === result[result.length - base._meta.bases.length] ?
+ base._meta.bases.length : 1 : 0;
+
+ return result;
+ }
+
+ function inherited(args, a, f){
+ var name, chains, bases, caller, meta, base, proto, opf, pos,
+ cache = this._inherited = this._inherited || {};
+
+ // crack arguments
+ if(typeof args == "string"){
+ name = args;
+ args = a;
+ a = f;
+ }
+ f = 0;
+
+ caller = args.callee;
+ name = name || caller.nom;
+ if(!name){
+ err("can't deduce a name to call inherited()", this.declaredClass);
+ }
+
+ meta = this.constructor._meta;
+ bases = meta.bases;
+
+ pos = cache.p;
+ if(name != cname){
+ // method
+ if(cache.c !== caller){
+ // cache bust
+ pos = 0;
+ base = bases[0];
+ meta = base._meta;
+ if(meta.hidden[name] !== caller){
+ // error detection
+ chains = meta.chains;
+ if(chains && typeof chains[name] == "string"){
+ err("calling chained method with inherited: " + name, this.declaredClass);
+ }
+ // find caller
+ do{
+ meta = base._meta;
+ proto = base.prototype;
+ if(meta && (proto[name] === caller && proto.hasOwnProperty(name) || meta.hidden[name] === caller)){
+ break;
+ }
+ }while(base = bases[++pos]); // intentional assignment
+ pos = base ? pos : -1;
+ }
+ }
+ // find next
+ base = bases[++pos];
+ if(base){
+ proto = base.prototype;
+ if(base._meta && proto.hasOwnProperty(name)){
+ f = proto[name];
+ }else{
+ opf = op[name];
+ do{
+ proto = base.prototype;
+ f = proto[name];
+ if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
+ break;
+ }
+ }while(base = bases[++pos]); // intentional assignment
+ }
+ }
+ f = base && f || op[name];
+ }else{
+ // constructor
+ if(cache.c !== caller){
+ // cache bust
+ pos = 0;
+ meta = bases[0]._meta;
+ if(meta && meta.ctor !== caller){
+ // error detection
+ chains = meta.chains;
+ if(!chains || chains.constructor !== "manual"){
+ err("calling chained constructor with inherited", this.declaredClass);
+ }
+ // find caller
+ while(base = bases[++pos]){ // intentional assignment
+ meta = base._meta;
+ if(meta && meta.ctor === caller){
+ break;
+ }
+ }
+ pos = base ? pos : -1;
+ }
+ }
+ // find next
+ while(base = bases[++pos]){ // intentional assignment
+ meta = base._meta;
+ f = meta ? meta.ctor : base;
+ if(f){
+ break;
+ }
+ }
+ f = base && f;
+ }
+
+ // cache the found super method
+ cache.c = f;
+ cache.p = pos;
+
+ // now we have the result
+ if(f){
+ return a === true ? f : f.apply(this, a || args);
+ }
+ // intentionally no return if a super method was not found
+ }
+
+ function getInherited(name, args){
+ if(typeof name == "string"){
+ return this.__inherited(name, args, true);
+ }
+ return this.__inherited(name, true);
+ }
+
+ function inherited__debug(args, a1, a2){
+ var f = this.getInherited(args, a1);
+ if(f){ return f.apply(this, a2 || a1 || args); }
+ // intentionally no return if a super method was not found
+ }
+
+ var inheritedImpl = dojo.config.isDebug ? inherited__debug : inherited;
+
+ // emulation of "instanceof"
+ function isInstanceOf(cls){
+ var bases = this.constructor._meta.bases;
+ for(var i = 0, l = bases.length; i < l; ++i){
+ if(bases[i] === cls){
+ return true;
+ }
+ }
+ return this instanceof cls;
+ }
+
+ function mixOwn(target, source){
+ // add props adding metadata for incoming functions skipping a constructor
+ for(var name in source){
+ if(name != cname && source.hasOwnProperty(name)){
+ target[name] = source[name];
+ }
+ }
+ if(has("bug-for-in-skips-shadowed")){
+ for(var extraNames= lang._extraNames, i= extraNames.length; i;){
+ name = extraNames[--i];
+ if(name != cname && source.hasOwnProperty(name)){
+ target[name] = source[name];
+ }
+ }
+ }
+ }
+
+ // implementation of safe mixin function
+ function safeMixin(target, source){
+ // summary:
+ // Mix in properties skipping a constructor and decorating functions
+ // like it is done by declare().
+ // target: Object
+ // Target object to accept new properties.
+ // source: Object
+ // Source object for new properties.
+ // description:
+ // This function is used to mix in properties like lang.mixin does,
+ // but it skips a constructor property and decorates functions like
+ // declare() does.
+ //
+ // It is meant to be used with classes and objects produced with
+ // declare. Functions mixed in with dojo.safeMixin can use
+ // this.inherited() like normal methods.
+ //
+ // This function is used to implement extend() method of a constructor
+ // produced with declare().
+ //
+ // example:
+ // | var A = declare(null, {
+ // | m1: function(){
+ // | console.log("A.m1");
+ // | },
+ // | m2: function(){
+ // | console.log("A.m2");
+ // | }
+ // | });
+ // | var B = declare(A, {
+ // | m1: function(){
+ // | this.inherited(arguments);
+ // | console.log("B.m1");
+ // | }
+ // | });
+ // | B.extend({
+ // | m2: function(){
+ // | this.inherited(arguments);
+ // | console.log("B.m2");
+ // | }
+ // | });
+ // | var x = new B();
+ // | dojo.safeMixin(x, {
+ // | m1: function(){
+ // | this.inherited(arguments);
+ // | console.log("X.m1");
+ // | },
+ // | m2: function(){
+ // | this.inherited(arguments);
+ // | console.log("X.m2");
+ // | }
+ // | });
+ // | x.m2();
+ // | // prints:
+ // | // A.m1
+ // | // B.m1
+ // | // X.m1
+
+ var name, t;
+ // add props adding metadata for incoming functions skipping a constructor
+ for(name in source){
+ t = source[name];
+ if((t !== op[name] || !(name in op)) && name != cname){
+ if(opts.call(t) == "[object Function]"){
+ // non-trivial function method => attach its name
+ t.nom = name;
+ }
+ target[name] = t;
+ }
+ }
+ if(has("bug-for-in-skips-shadowed")){
+ for(var extraNames= lang._extraNames, i= extraNames.length; i;){
+ name = extraNames[--i];
+ t = source[name];
+ if((t !== op[name] || !(name in op)) && name != cname){
+ if(opts.call(t) == "[object Function]"){
+ // non-trivial function method => attach its name
+ t.nom = name;
+ }
+ target[name] = t;
+ }
+ }
+ }
+ return target;
+ }
+
+ function extend(source){
+ declare.safeMixin(this.prototype, source);
+ return this;
+ }
+
+ function createSubclass(mixins, props){
+ return declare([this].concat(mixins), props || {});
+ }
+
+ // chained constructor compatible with the legacy declare()
+ function chainedConstructor(bases, ctorSpecial){
+ return function(){
+ var a = arguments, args = a, a0 = a[0], f, i, m,
+ l = bases.length, preArgs;
+
+ if(!(this instanceof a.callee)){
+ // not called via new, so force it
+ return applyNew(a);
+ }
+
+ //this._inherited = {};
+ // perform the shaman's rituals of the original declare()
+ // 1) call two types of the preamble
+ if(ctorSpecial && (a0 && a0.preamble || this.preamble)){
+ // full blown ritual
+ preArgs = new Array(bases.length);
+ // prepare parameters
+ preArgs[0] = a;
+ for(i = 0;;){
+ // process the preamble of the 1st argument
+ a0 = a[0];
+ if(a0){
+ f = a0.preamble;
+ if(f){
+ a = f.apply(this, a) || a;
+ }
+ }
+ // process the preamble of this class
+ f = bases[i].prototype;
+ f = f.hasOwnProperty("preamble") && f.preamble;
+ if(f){
+ a = f.apply(this, a) || a;
+ }
+ // one peculiarity of the preamble:
+ // it is called if it is not needed,
+ // e.g., there is no constructor to call
+ // let's watch for the last constructor
+ // (see ticket #9795)
+ if(++i == l){
+ break;
+ }
+ preArgs[i] = a;
+ }
+ }
+ // 2) call all non-trivial constructors using prepared arguments
+ for(i = l - 1; i >= 0; --i){
+ f = bases[i];
+ m = f._meta;
+ f = m ? m.ctor : f;
+ if(f){
+ f.apply(this, preArgs ? preArgs[i] : a);
+ }
+ }
+ // 3) continue the original ritual: call the postscript
+ f = this.postscript;
+ if(f){
+ f.apply(this, args);
+ }
+ };
+ }
+
+
+ // chained constructor compatible with the legacy declare()
+ function singleConstructor(ctor, ctorSpecial){
+ return function(){
+ var a = arguments, t = a, a0 = a[0], f;
+
+ if(!(this instanceof a.callee)){
+ // not called via new, so force it
+ return applyNew(a);
+ }
+
+ //this._inherited = {};
+ // perform the shaman's rituals of the original declare()
+ // 1) call two types of the preamble
+ if(ctorSpecial){
+ // full blown ritual
+ if(a0){
+ // process the preamble of the 1st argument
+ f = a0.preamble;
+ if(f){
+ t = f.apply(this, t) || t;
+ }
+ }
+ f = this.preamble;
+ if(f){
+ // process the preamble of this class
+ f.apply(this, t);
+ // one peculiarity of the preamble:
+ // it is called even if it is not needed,
+ // e.g., there is no constructor to call
+ // let's watch for the last constructor
+ // (see ticket #9795)
+ }
+ }
+ // 2) call a constructor
+ if(ctor){
+ ctor.apply(this, a);
+ }
+ // 3) continue the original ritual: call the postscript
+ f = this.postscript;
+ if(f){
+ f.apply(this, a);
+ }
+ };
+ }
+
+ // plain vanilla constructor (can use inherited() to call its base constructor)
+ function simpleConstructor(bases){
+ return function(){
+ var a = arguments, i = 0, f, m;
+
+ if(!(this instanceof a.callee)){
+ // not called via new, so force it
+ return applyNew(a);
+ }
+
+ //this._inherited = {};
+ // perform the shaman's rituals of the original declare()
+ // 1) do not call the preamble
+ // 2) call the top constructor (it can use this.inherited())
+ for(; f = bases[i]; ++i){ // intentional assignment
+ m = f._meta;
+ f = m ? m.ctor : f;
+ if(f){
+ f.apply(this, a);
+ break;
+ }
+ }
+ // 3) call the postscript
+ f = this.postscript;
+ if(f){
+ f.apply(this, a);
+ }
+ };
+ }
+
+ function chain(name, bases, reversed){
+ return function(){
+ var b, m, f, i = 0, step = 1;
+ if(reversed){
+ i = bases.length - 1;
+ step = -1;
+ }
+ for(; b = bases[i]; i += step){ // intentional assignment
+ m = b._meta;
+ f = (m ? m.hidden : b.prototype)[name];
+ if(f){
+ f.apply(this, arguments);
+ }
+ }
+ };
+ }
+
+ // forceNew(ctor)
+ // return a new object that inherits from ctor.prototype but
+ // without actually running ctor on the object.
+ function forceNew(ctor){
+ // create object with correct prototype using a do-nothing
+ // constructor
+ xtor.prototype = ctor.prototype;
+ var t = new xtor;
+ xtor.prototype = null; // clean up
+ return t;
+ }
+
+ // applyNew(args)
+ // just like 'new ctor()' except that the constructor and its arguments come
+ // from args, which must be an array or an arguments object
+ function applyNew(args){
+ // create an object with ctor's prototype but without
+ // calling ctor on it.
+ var ctor = args.callee, t = forceNew(ctor);
+ // execute the real constructor on the new object
+ ctor.apply(t, args);
+ return t;
+ }
+
+ function declare(className, superclass, props){
+ // summary:
+ // Create a feature-rich constructor from compact notation.
+ // className: String?
+ // The optional name of the constructor (loosely, a "class")
+ // stored in the "declaredClass" property in the created prototype.
+ // It will be used as a global name for a created constructor.
+ // superclass: Function|Function[]
+ // May be null, a Function, or an Array of Functions. This argument
+ // specifies a list of bases (the left-most one is the most deepest
+ // base).
+ // props: Object
+ // An object whose properties are copied to the created prototype.
+ // Add an instance-initialization function by making it a property
+ // named "constructor".
+ // returns: dojo/_base/declare.__DeclareCreatedObject
+ // New constructor function.
+ // description:
+ // Create a constructor using a compact notation for inheritance and
+ // prototype extension.
+ //
+ // Mixin ancestors provide a type of multiple inheritance.
+ // Prototypes of mixin ancestors are copied to the new class:
+ // changes to mixin prototypes will not affect classes to which
+ // they have been mixed in.
+ //
+ // Ancestors can be compound classes created by this version of
+ // declare(). In complex cases all base classes are going to be
+ // linearized according to C3 MRO algorithm
+ // (see http://www.python.org/download/releases/2.3/mro/ for more
+ // details).
+ //
+ // "className" is cached in "declaredClass" property of the new class,
+ // if it was supplied. The immediate super class will be cached in
+ // "superclass" property of the new class.
+ //
+ // Methods in "props" will be copied and modified: "nom" property
+ // (the declared name of the method) will be added to all copied
+ // functions to help identify them for the internal machinery. Be
+ // very careful, while reusing methods: if you use the same
+ // function under different names, it can produce errors in some
+ // cases.
+ //
+ // It is possible to use constructors created "manually" (without
+ // declare()) as bases. They will be called as usual during the
+ // creation of an instance, their methods will be chained, and even
+ // called by "this.inherited()".
+ //
+ // Special property "-chains-" governs how to chain methods. It is
+ // a dictionary, which uses method names as keys, and hint strings
+ // as values. If a hint string is "after", this method will be
+ // called after methods of its base classes. If a hint string is
+ // "before", this method will be called before methods of its base
+ // classes.
+ //
+ // If "constructor" is not mentioned in "-chains-" property, it will
+ // be chained using the legacy mode: using "after" chaining,
+ // calling preamble() method before each constructor, if available,
+ // and calling postscript() after all constructors were executed.
+ // If the hint is "after", it is chained as a regular method, but
+ // postscript() will be called after the chain of constructors.
+ // "constructor" cannot be chained "before", but it allows
+ // a special hint string: "manual", which means that constructors
+ // are not going to be chained in any way, and programmer will call
+ // them manually using this.inherited(). In the latter case
+ // postscript() will be called after the construction.
+ //
+ // All chaining hints are "inherited" from base classes and
+ // potentially can be overridden. Be very careful when overriding
+ // hints! Make sure that all chained methods can work in a proposed
+ // manner of chaining.
+ //
+ // Once a method was chained, it is impossible to unchain it. The
+ // only exception is "constructor". You don't need to define a
+ // method in order to supply a chaining hint.
+ //
+ // If a method is chained, it cannot use this.inherited() because
+ // all other methods in the hierarchy will be called automatically.
+ //
+ // Usually constructors and initializers of any kind are chained
+ // using "after" and destructors of any kind are chained as
+ // "before". Note that chaining assumes that chained methods do not
+ // return any value: any returned value will be discarded.
+ //
+ // example:
+ // | declare("my.classes.bar", my.classes.foo, {
+ // | // properties to be added to the class prototype
+ // | someValue: 2,
+ // | // initialization function
+ // | constructor: function(){
+ // | this.myComplicatedObject = new ReallyComplicatedObject();
+ // | },
+ // | // other functions
+ // | someMethod: function(){
+ // | doStuff();
+ // | }
+ // | });
+ //
+ // example:
+ // | var MyBase = declare(null, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var MyClass1 = declare(MyBase, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var MyClass2 = declare(MyBase, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var MyDiamond = declare([MyClass1, MyClass2], {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ //
+ // example:
+ // | var F = function(){ console.log("raw constructor"); };
+ // | F.prototype.method = function(){
+ // | console.log("raw method");
+ // | };
+ // | var A = declare(F, {
+ // | constructor: function(){
+ // | console.log("A.constructor");
+ // | },
+ // | method: function(){
+ // | console.log("before calling F.method...");
+ // | this.inherited(arguments);
+ // | console.log("...back in A");
+ // | }
+ // | });
+ // | new A().method();
+ // | // will print:
+ // | // raw constructor
+ // | // A.constructor
+ // | // before calling F.method...
+ // | // raw method
+ // | // ...back in A
+ //
+ // example:
+ // | var A = declare(null, {
+ // | "-chains-": {
+ // | destroy: "before"
+ // | }
+ // | });
+ // | var B = declare(A, {
+ // | constructor: function(){
+ // | console.log("B.constructor");
+ // | },
+ // | destroy: function(){
+ // | console.log("B.destroy");
+ // | }
+ // | });
+ // | var C = declare(B, {
+ // | constructor: function(){
+ // | console.log("C.constructor");
+ // | },
+ // | destroy: function(){
+ // | console.log("C.destroy");
+ // | }
+ // | });
+ // | new C().destroy();
+ // | // prints:
+ // | // B.constructor
+ // | // C.constructor
+ // | // C.destroy
+ // | // B.destroy
+ //
+ // example:
+ // | var A = declare(null, {
+ // | "-chains-": {
+ // | constructor: "manual"
+ // | }
+ // | });
+ // | var B = declare(A, {
+ // | constructor: function(){
+ // | // ...
+ // | // call the base constructor with new parameters
+ // | this.inherited(arguments, [1, 2, 3]);
+ // | // ...
+ // | }
+ // | });
+ //
+ // example:
+ // | var A = declare(null, {
+ // | "-chains-": {
+ // | m1: "before"
+ // | },
+ // | m1: function(){
+ // | console.log("A.m1");
+ // | },
+ // | m2: function(){
+ // | console.log("A.m2");
+ // | }
+ // | });
+ // | var B = declare(A, {
+ // | "-chains-": {
+ // | m2: "after"
+ // | },
+ // | m1: function(){
+ // | console.log("B.m1");
+ // | },
+ // | m2: function(){
+ // | console.log("B.m2");
+ // | }
+ // | });
+ // | var x = new B();
+ // | x.m1();
+ // | // prints:
+ // | // B.m1
+ // | // A.m1
+ // | x.m2();
+ // | // prints:
+ // | // A.m2
+ // | // B.m2
+
+ // crack parameters
+ if(typeof className != "string"){
+ props = superclass;
+ superclass = className;
+ className = "";
+ }
+ props = props || {};
+
+ var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass;
+
+ // build a prototype
+ if(opts.call(superclass) == "[object Array]"){
+ // C3 MRO
+ bases = c3mro(superclass, className);
+ t = bases[0];
+ mixins = bases.length - t;
+ superclass = bases[mixins];
+ }else{
+ bases = [0];
+ if(superclass){
+ if(opts.call(superclass) == "[object Function]"){
+ t = superclass._meta;
+ bases = bases.concat(t ? t.bases : superclass);
+ }else{
+ err("base class is not a callable constructor.", className);
+ }
+ }else if(superclass !== null){
+ err("unknown base class. Did you use dojo.require to pull it in?", className);
+ }
+ }
+ if(superclass){
+ for(i = mixins - 1;; --i){
+ proto = forceNew(superclass);
+ if(!i){
+ // stop if nothing to add (the last base)
+ break;
+ }
+ // mix in properties
+ t = bases[i];
+ (t._meta ? mixOwn : mix)(proto, t.prototype);
+ // chain in new constructor
+ ctor = new Function;
+ ctor.superclass = superclass;
+ ctor.prototype = proto;
+ superclass = proto.constructor = ctor;
+ }
+ }else{
+ proto = {};
+ }
+ // add all properties
+ declare.safeMixin(proto, props);
+ // add constructor
+ t = props.constructor;
+ if(t !== op.constructor){
+ t.nom = cname;
+ proto.constructor = t;
+ }
+
+ // collect chains and flags
+ for(i = mixins - 1; i; --i){ // intentional assignment
+ t = bases[i]._meta;
+ if(t && t.chains){
+ chains = mix(chains || {}, t.chains);
+ }
+ }
+ if(proto["-chains-"]){
+ chains = mix(chains || {}, proto["-chains-"]);
+ }
+
+ // build ctor
+ t = !chains || !chains.hasOwnProperty(cname);
+ bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) :
+ (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t));
+
+ // add meta information to the constructor
+ ctor._meta = {bases: bases, hidden: props, chains: chains,
+ parents: parents, ctor: props.constructor};
+ ctor.superclass = superclass && superclass.prototype;
+ ctor.extend = extend;
+ ctor.createSubclass = createSubclass;
+ ctor.prototype = proto;
+ proto.constructor = ctor;
+
+ // add "standard" methods to the prototype
+ proto.getInherited = getInherited;
+ proto.isInstanceOf = isInstanceOf;
+ proto.inherited = inheritedImpl;
+ proto.__inherited = inherited;
+
+ // add name if specified
+ if(className){
+ proto.declaredClass = className;
+ lang.setObject(className, ctor);
+ }
+
+ // build chains and add them to the prototype
+ if(chains){
+ for(name in chains){
+ if(proto[name] && typeof chains[name] == "string" && name != cname){
+ t = proto[name] = chain(name, bases, chains[name] === "after");
+ t.nom = name;
+ }
+ }
+ }
+ // chained methods do not return values
+ // no need to chain "invisible" functions
+
+ return ctor; // Function
+ }
+
+ /*=====
+ declare.__DeclareCreatedObject = {
+ // summary:
+ // dojo/_base/declare() returns a constructor `C`. `new C()` returns an Object with the following
+ // methods, in addition to the methods and properties specified via the arguments passed to declare().
+
+ inherited: function(name, args, newArgs){
+ // summary:
+ // Calls a super method.
+ // name: String?
+ // The optional method name. Should be the same as the caller's
+ // name. Usually "name" is specified in complex dynamic cases, when
+ // the calling method was dynamically added, undecorated by
+ // declare(), and it cannot be determined.
+ // args: Arguments
+ // The caller supply this argument, which should be the original
+ // "arguments".
+ // newArgs: Object?
+ // If "true", the found function will be returned without
+ // executing it.
+ // If Array, it will be used to call a super method. Otherwise
+ // "args" will be used.
+ // returns:
+ // Whatever is returned by a super method, or a super method itself,
+ // if "true" was specified as newArgs.
+ // description:
+ // This method is used inside method of classes produced with
+ // declare() to call a super method (next in the chain). It is
+ // used for manually controlled chaining. Consider using the regular
+ // chaining, because it is faster. Use "this.inherited()" only in
+ // complex cases.
+ //
+ // This method cannot me called from automatically chained
+ // constructors including the case of a special (legacy)
+ // constructor chaining. It cannot be called from chained methods.
+ //
+ // If "this.inherited()" cannot find the next-in-chain method, it
+ // does nothing and returns "undefined". The last method in chain
+ // can be a default method implemented in Object, which will be
+ // called last.
+ //
+ // If "name" is specified, it is assumed that the method that
+ // received "args" is the parent method for this call. It is looked
+ // up in the chain list and if it is found the next-in-chain method
+ // is called. If it is not found, the first-in-chain method is
+ // called.
+ //
+ // If "name" is not specified, it will be derived from the calling
+ // method (using a methoid property "nom").
+ //
+ // example:
+ // | var B = declare(A, {
+ // | method1: function(a, b, c){
+ // | this.inherited(arguments);
+ // | },
+ // | method2: function(a, b){
+ // | return this.inherited(arguments, [a + b]);
+ // | }
+ // | });
+ // | // next method is not in the chain list because it is added
+ // | // manually after the class was created.
+ // | B.prototype.method3 = function(){
+ // | console.log("This is a dynamically-added method.");
+ // | this.inherited("method3", arguments);
+ // | };
+ // example:
+ // | var B = declare(A, {
+ // | method: function(a, b){
+ // | var super = this.inherited(arguments, true);
+ // | // ...
+ // | if(!super){
+ // | console.log("there is no super method");
+ // | return 0;
+ // | }
+ // | return super.apply(this, arguments);
+ // | }
+ // | });
+ return {}; // Object
+ },
+
+ getInherited: function(name, args){
+ // summary:
+ // Returns a super method.
+ // name: String?
+ // The optional method name. Should be the same as the caller's
+ // name. Usually "name" is specified in complex dynamic cases, when
+ // the calling method was dynamically added, undecorated by
+ // declare(), and it cannot be determined.
+ // args: Arguments
+ // The caller supply this argument, which should be the original
+ // "arguments".
+ // returns:
+ // Returns a super method (Function) or "undefined".
+ // description:
+ // This method is a convenience method for "this.inherited()".
+ // It uses the same algorithm but instead of executing a super
+ // method, it returns it, or "undefined" if not found.
+ //
+ // example:
+ // | var B = declare(A, {
+ // | method: function(a, b){
+ // | var super = this.getInherited(arguments);
+ // | // ...
+ // | if(!super){
+ // | console.log("there is no super method");
+ // | return 0;
+ // | }
+ // | return super.apply(this, arguments);
+ // | }
+ // | });
+ return {}; // Object
+ },
+
+ isInstanceOf: function(cls){
+ // summary:
+ // Checks the inheritance chain to see if it is inherited from this
+ // class.
+ // cls: Function
+ // Class constructor.
+ // returns:
+ // "true", if this object is inherited from this class, "false"
+ // otherwise.
+ // description:
+ // This method is used with instances of classes produced with
+ // declare() to determine of they support a certain interface or
+ // not. It models "instanceof" operator.
+ //
+ // example:
+ // | var A = declare(null, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var B = declare(null, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var C = declare([A, B], {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // | var D = declare(A, {
+ // | // constructor, properties, and methods go here
+ // | // ...
+ // | });
+ // |
+ // | var a = new A(), b = new B(), c = new C(), d = new D();
+ // |
+ // | console.log(a.isInstanceOf(A)); // true
+ // | console.log(b.isInstanceOf(A)); // false
+ // | console.log(c.isInstanceOf(A)); // true
+ // | console.log(d.isInstanceOf(A)); // true
+ // |
+ // | console.log(a.isInstanceOf(B)); // false
+ // | console.log(b.isInstanceOf(B)); // true
+ // | console.log(c.isInstanceOf(B)); // true
+ // | console.log(d.isInstanceOf(B)); // false
+ // |
+ // | console.log(a.isInstanceOf(C)); // false
+ // | console.log(b.isInstanceOf(C)); // false
+ // | console.log(c.isInstanceOf(C)); // true
+ // | console.log(d.isInstanceOf(C)); // false
+ // |
+ // | console.log(a.isInstanceOf(D)); // false
+ // | console.log(b.isInstanceOf(D)); // false
+ // | console.log(c.isInstanceOf(D)); // false
+ // | console.log(d.isInstanceOf(D)); // true
+ return {}; // Object
+ },
+
+ extend: function(source){
+ // summary:
+ // Adds all properties and methods of source to constructor's
+ // prototype, making them available to all instances created with
+ // constructor. This method is specific to constructors created with
+ // declare().
+ // source: Object
+ // Source object which properties are going to be copied to the
+ // constructor's prototype.
+ // description:
+ // Adds source properties to the constructor's prototype. It can
+ // override existing properties.
+ //
+ // This method is similar to dojo.extend function, but it is specific
+ // to constructors produced by declare(). It is implemented
+ // using dojo.safeMixin, and it skips a constructor property,
+ // and properly decorates copied functions.
+ //
+ // example:
+ // | var A = declare(null, {
+ // | m1: function(){},
+ // | s1: "Popokatepetl"
+ // | });
+ // | A.extend({
+ // | m1: function(){},
+ // | m2: function(){},
+ // | f1: true,
+ // | d1: 42
+ // | });
+ },
+
+ createSubclass: function(mixins, props){
+ // summary:
+ // Create a subclass of the declared class from a list of base classes.
+ // mixins: Function[]
+ // Specifies a list of bases (the left-most one is the most deepest
+ // base).
+ // props: Object?
+ // An optional object whose properties are copied to the created prototype.
+ // returns: dojo/_base/declare.__DeclareCreatedObject
+ // New constructor function.
+ // description:
+ // Create a constructor using a compact notation for inheritance and
+ // prototype extension.
+ //
+ // Mixin ancestors provide a type of multiple inheritance.
+ // Prototypes of mixin ancestors are copied to the new class:
+ // changes to mixin prototypes will not affect classes to which
+ // they have been mixed in.
+ //
+ // example:
+ // | var A = declare(null, {
+ // | m1: function(){},
+ // | s1: "bar"
+ // | });
+ // | var B = declare(null, {
+ // | m2: function(){},
+ // | s2: "foo"
+ // | });
+ // | var C = declare(null, {
+ // | });
+ // | var D1 = A.createSubclass([B, C], {
+ // | m1: function(){},
+ // | d1: 42
+ // | });
+ // | var d1 = new D1();
+ // |
+ // | // this is equivalent to:
+ // | var D2 = declare([A, B, C], {
+ // | m1: function(){},
+ // | d1: 42
+ // | });
+ // | var d2 = new D2();
+ }
+ };
+ =====*/
+
+ // For back-compat, remove for 2.0
+ dojo.safeMixin = declare.safeMixin = safeMixin;
+ dojo.declare = declare;
+
+ return declare;
+});
diff --git a/src/main/resources/static/dojo/_base/event.js b/src/main/resources/static/dojo/_base/event.js
new file mode 100644
index 0000000000000000000000000000000000000000..3056d07ec024f200d8fa37544ea4b1425c19cd32
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/event.js
@@ -0,0 +1,59 @@
+define(["./kernel", "../on", "../has", "../dom-geometry"], function(dojo, on, has, dom){
+ // module:
+ // dojo/_base/event
+
+ if(on._fixEvent){
+ var fixEvent = on._fixEvent;
+ on._fixEvent = function(evt, se){
+ // add some additional normalization for back-compat, this isn't in on.js because it is somewhat more expensive
+ evt = fixEvent(evt, se);
+ if(evt){
+ dom.normalizeEvent(evt);
+ }
+ return evt;
+ };
+ }
+
+ var ret = {
+ // summary:
+ // This module defines dojo DOM event API. Usually you should use dojo/on, and evt.stopPropagation() +
+ // evt.preventDefault(), rather than this module.
+
+ fix: function(/*Event*/ evt, /*DOMNode*/ sender){
+ // summary:
+ // normalizes properties on the event object including event
+ // bubbling methods, keystroke normalization, and x/y positions
+ // evt: Event
+ // native event object
+ // sender: DOMNode
+ // node to treat as "currentTarget"
+ if(on._fixEvent){
+ return on._fixEvent(evt, sender);
+ }
+ return evt; // Event
+ },
+
+ stop: function(/*Event*/ evt){
+ // summary:
+ // prevents propagation and clobbers the default action of the
+ // passed event
+ // evt: Event
+ // The event object. If omitted, window.event is used on IE.
+ if(has("dom-addeventlistener") || (evt && evt.preventDefault)){
+ evt.preventDefault();
+ evt.stopPropagation();
+ }else{
+ evt = evt || window.event;
+ evt.cancelBubble = true;
+ on._preventDefault.call(evt);
+ }
+ }
+ };
+
+ if(has("extend-dojo")){
+ dojo.fixEvent = ret.fix;
+ dojo.stopEvent = ret.stop;
+ }
+
+ return ret;
+});
diff --git a/src/main/resources/static/dojo/_base/fx.js b/src/main/resources/static/dojo/_base/fx.js
new file mode 100644
index 0000000000000000000000000000000000000000..a17b2361fc00b902c6bd5688e1c3fae88eb4e560
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/fx.js
@@ -0,0 +1,670 @@
+define(["./kernel", "./config", /*===== "./declare", =====*/ "./lang", "../Evented", "./Color", "../aspect", "../sniff", "../dom", "../dom-style"],
+ function(dojo, config, /*===== declare, =====*/ lang, Evented, Color, aspect, has, dom, style){
+ // module:
+ // dojo/_base/fx
+ // notes:
+ // Animation loosely package based on Dan Pupius' work, contributed under CLA; see
+ // http://pupius.co.uk/js/Toolkit.Drawing.js
+
+ var _mixin = lang.mixin;
+
+ // Module export
+ var basefx = {
+ // summary:
+ // This module defines the base dojo/_base/fx implementation.
+ };
+
+ var _Line = basefx._Line = function(/*int*/ start, /*int*/ end){
+ // summary:
+ // Object used to generate values from a start value to an end value
+ // start: int
+ // Beginning value for range
+ // end: int
+ // Ending value for range
+ this.start = start;
+ this.end = end;
+ };
+
+ _Line.prototype.getValue = function(/*float*/ n){
+ // summary:
+ // Returns the point on the line
+ // n:
+ // a floating point number greater than 0 and less than 1
+ return ((this.end - this.start) * n) + this.start; // Decimal
+ };
+
+ var Animation = basefx.Animation = function(args){
+ // summary:
+ // A generic animation class that fires callbacks into its handlers
+ // object at various states.
+ // description:
+ // A generic animation class that fires callbacks into its handlers
+ // object at various states. Nearly all dojo animation functions
+ // return an instance of this method, usually without calling the
+ // .play() method beforehand. Therefore, you will likely need to
+ // call .play() on instances of `Animation` when one is
+ // returned.
+ // args: Object
+ // The 'magic argument', mixing all the properties into this
+ // animation instance.
+
+ _mixin(this, args);
+ if(lang.isArray(this.curve)){
+ this.curve = new _Line(this.curve[0], this.curve[1]);
+ }
+
+ };
+ Animation.prototype = new Evented();
+
+ lang.extend(Animation, {
+ // duration: Integer
+ // The time in milliseconds the animation will take to run
+ duration: 350,
+
+ /*=====
+ // curve: _Line|Array
+ // A two element array of start and end values, or a `_Line` instance to be
+ // used in the Animation.
+ curve: null,
+
+ // easing: Function?
+ // A Function to adjust the acceleration (or deceleration) of the progress
+ // across a _Line
+ easing: null,
+ =====*/
+
+ // repeat: Integer?
+ // The number of times to loop the animation
+ repeat: 0,
+
+ // rate: Integer?
+ // the time in milliseconds to wait before advancing to next frame
+ // (used as a fps timer: 1000/rate = fps)
+ rate: 20 /* 50 fps */,
+
+ /*=====
+ // delay: Integer?
+ // The time in milliseconds to wait before starting animation after it
+ // has been .play()'ed
+ delay: null,
+
+ // beforeBegin: Event?
+ // Synthetic event fired before a Animation begins playing (synchronous)
+ beforeBegin: null,
+
+ // onBegin: Event?
+ // Synthetic event fired as a Animation begins playing (useful?)
+ onBegin: null,
+
+ // onAnimate: Event?
+ // Synthetic event fired at each interval of the Animation
+ onAnimate: null,
+
+ // onEnd: Event?
+ // Synthetic event fired after the final frame of the Animation
+ onEnd: null,
+
+ // onPlay: Event?
+ // Synthetic event fired any time the Animation is play()'ed
+ onPlay: null,
+
+ // onPause: Event?
+ // Synthetic event fired when the Animation is paused
+ onPause: null,
+
+ // onStop: Event
+ // Synthetic event fires when the Animation is stopped
+ onStop: null,
+
+ =====*/
+
+ _percent: 0,
+ _startRepeatCount: 0,
+
+ _getStep: function(){
+ var _p = this._percent,
+ _e = this.easing
+ ;
+ return _e ? _e(_p) : _p;
+ },
+ _fire: function(/*Event*/ evt, /*Array?*/ args){
+ // summary:
+ // Convenience function. Fire event "evt" and pass it the
+ // arguments specified in "args".
+ // description:
+ // Convenience function. Fire event "evt" and pass it the
+ // arguments specified in "args".
+ // Fires the callback in the scope of this Animation
+ // instance.
+ // evt:
+ // The event to fire.
+ // args:
+ // The arguments to pass to the event.
+ var a = args||[];
+ if(this[evt]){
+ if(config.debugAtAllCosts){
+ this[evt].apply(this, a);
+ }else{
+ try{
+ this[evt].apply(this, a);
+ }catch(e){
+ // squelch and log because we shouldn't allow exceptions in
+ // synthetic event handlers to cause the internal timer to run
+ // amuck, potentially pegging the CPU. I'm not a fan of this
+ // squelch, but hopefully logging will make it clear what's
+ // going on
+ console.error("exception in animation handler for:", evt);
+ console.error(e);
+ }
+ }
+ }
+ return this; // Animation
+ },
+
+ play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
+ // summary:
+ // Start the animation.
+ // delay:
+ // How many milliseconds to delay before starting.
+ // gotoStart:
+ // If true, starts the animation from the beginning; otherwise,
+ // starts it from its current position.
+ // returns: Animation
+ // The instance to allow chaining.
+
+ var _t = this;
+ if(_t._delayTimer){ _t._clearTimer(); }
+ if(gotoStart){
+ _t._stopTimer();
+ _t._active = _t._paused = false;
+ _t._percent = 0;
+ }else if(_t._active && !_t._paused){
+ return _t;
+ }
+
+ _t._fire("beforeBegin", [_t.node]);
+
+ var de = delay || _t.delay,
+ _p = lang.hitch(_t, "_play", gotoStart);
+
+ if(de > 0){
+ _t._delayTimer = setTimeout(_p, de);
+ return _t;
+ }
+ _p();
+ return _t; // Animation
+ },
+
+ _play: function(gotoStart){
+ var _t = this;
+ if(_t._delayTimer){ _t._clearTimer(); }
+ _t._startTime = new Date().valueOf();
+ if(_t._paused){
+ _t._startTime -= _t.duration * _t._percent;
+ }
+
+ _t._active = true;
+ _t._paused = false;
+ var value = _t.curve.getValue(_t._getStep());
+ if(!_t._percent){
+ if(!_t._startRepeatCount){
+ _t._startRepeatCount = _t.repeat;
+ }
+ _t._fire("onBegin", [value]);
+ }
+
+ _t._fire("onPlay", [value]);
+
+ _t._cycle();
+ return _t; // Animation
+ },
+
+ pause: function(){
+ // summary:
+ // Pauses a running animation.
+ var _t = this;
+ if(_t._delayTimer){ _t._clearTimer(); }
+ _t._stopTimer();
+ if(!_t._active){ return _t; /*Animation*/ }
+ _t._paused = true;
+ _t._fire("onPause", [_t.curve.getValue(_t._getStep())]);
+ return _t; // Animation
+ },
+
+ gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
+ // summary:
+ // Sets the progress of the animation.
+ // percent:
+ // A percentage in decimal notation (between and including 0.0 and 1.0).
+ // andPlay:
+ // If true, play the animation after setting the progress.
+ var _t = this;
+ _t._stopTimer();
+ _t._active = _t._paused = true;
+ _t._percent = percent;
+ if(andPlay){ _t.play(); }
+ return _t; // Animation
+ },
+
+ stop: function(/*boolean?*/ gotoEnd){
+ // summary:
+ // Stops a running animation.
+ // gotoEnd:
+ // If true, the animation will end.
+ var _t = this;
+ if(_t._delayTimer){ _t._clearTimer(); }
+ if(!_t._timer){ return _t; /* Animation */ }
+ _t._stopTimer();
+ if(gotoEnd){
+ _t._percent = 1;
+ }
+ _t._fire("onStop", [_t.curve.getValue(_t._getStep())]);
+ _t._active = _t._paused = false;
+ return _t; // Animation
+ },
+
+ status: function(){
+ // summary:
+ // Returns a string token representation of the status of
+ // the animation, one of: "paused", "playing", "stopped"
+ if(this._active){
+ return this._paused ? "paused" : "playing"; // String
+ }
+ return "stopped"; // String
+ },
+
+ _cycle: function(){
+ var _t = this;
+ if(_t._active){
+ var curr = new Date().valueOf();
+ // Allow durations of 0 (instant) by setting step to 1 - see #13798
+ var step = _t.duration === 0 ? 1 : (curr - _t._startTime) / (_t.duration);
+
+ if(step >= 1){
+ step = 1;
+ }
+ _t._percent = step;
+
+ // Perform easing
+ if(_t.easing){
+ step = _t.easing(step);
+ }
+
+ _t._fire("onAnimate", [_t.curve.getValue(step)]);
+
+ if(_t._percent < 1){
+ _t._startTimer();
+ }else{
+ _t._active = false;
+
+ if(_t.repeat > 0){
+ _t.repeat--;
+ _t.play(null, true);
+ }else if(_t.repeat == -1){
+ _t.play(null, true);
+ }else{
+ if(_t._startRepeatCount){
+ _t.repeat = _t._startRepeatCount;
+ _t._startRepeatCount = 0;
+ }
+ }
+ _t._percent = 0;
+ _t._fire("onEnd", [_t.node]);
+ !_t.repeat && _t._stopTimer();
+ }
+ }
+ return _t; // Animation
+ },
+
+ _clearTimer: function(){
+ // summary:
+ // Clear the play delay timer
+ clearTimeout(this._delayTimer);
+ delete this._delayTimer;
+ }
+
+ });
+
+ // the local timer, stubbed into all Animation instances
+ var ctr = 0,
+ timer = null,
+ runner = {
+ run: function(){}
+ };
+
+ lang.extend(Animation, {
+
+ _startTimer: function(){
+ if(!this._timer){
+ this._timer = aspect.after(runner, "run", lang.hitch(this, "_cycle"), true);
+ ctr++;
+ }
+ if(!timer){
+ timer = setInterval(lang.hitch(runner, "run"), this.rate);
+ }
+ },
+
+ _stopTimer: function(){
+ if(this._timer){
+ this._timer.remove();
+ this._timer = null;
+ ctr--;
+ }
+ if(ctr <= 0){
+ clearInterval(timer);
+ timer = null;
+ ctr = 0;
+ }
+ }
+
+ });
+
+ var _makeFadeable =
+ has("ie") ? function(node){
+ // only set the zoom if the "tickle" value would be the same as the
+ // default
+ var ns = node.style;
+ // don't set the width to auto if it didn't already cascade that way.
+ // We don't want to f anyones designs
+ if(!ns.width.length && style.get(node, "width") == "auto"){
+ ns.width = "auto";
+ }
+ } :
+ function(){};
+
+ basefx._fade = function(/*Object*/ args){
+ // summary:
+ // Returns an animation that will fade the node defined by
+ // args.node from the start to end values passed (args.start
+ // args.end) (end is mandatory, start is optional)
+
+ args.node = dom.byId(args.node);
+ var fArgs = _mixin({ properties: {} }, args),
+ props = (fArgs.properties.opacity = {});
+
+ props.start = !("start" in fArgs) ?
+ function(){
+ return +style.get(fArgs.node, "opacity")||0;
+ } : fArgs.start;
+ props.end = fArgs.end;
+
+ var anim = basefx.animateProperty(fArgs);
+ aspect.after(anim, "beforeBegin", lang.partial(_makeFadeable, fArgs.node), true);
+
+ return anim; // Animation
+ };
+
+ /*=====
+ var __FadeArgs = declare(null, {
+ // node: DOMNode|String
+ // The node referenced in the animation
+ // duration: Integer?
+ // Duration of the animation in milliseconds.
+ // easing: Function?
+ // An easing function.
+ });
+ =====*/
+
+ basefx.fadeIn = function(/*__FadeArgs*/ args){
+ // summary:
+ // Returns an animation that will fade node defined in 'args' from
+ // its current opacity to fully opaque.
+ return basefx._fade(_mixin({ end: 1 }, args)); // Animation
+ };
+
+ basefx.fadeOut = function(/*__FadeArgs*/ args){
+ // summary:
+ // Returns an animation that will fade node defined in 'args'
+ // from its current opacity to fully transparent.
+ return basefx._fade(_mixin({ end: 0 }, args)); // Animation
+ };
+
+ basefx._defaultEasing = function(/*Decimal?*/ n){
+ // summary:
+ // The default easing function for Animation(s)
+ return 0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2); // Decimal
+ };
+
+ var PropLine = function(properties){
+ // PropLine is an internal class which is used to model the values of
+ // an a group of CSS properties across an animation lifecycle. In
+ // particular, the "getValue" function handles getting interpolated
+ // values between start and end for a particular CSS value.
+ this._properties = properties;
+ for(var p in properties){
+ var prop = properties[p];
+ if(prop.start instanceof Color){
+ // create a reusable temp color object to keep intermediate results
+ prop.tempColor = new Color();
+ }
+ }
+ };
+
+ PropLine.prototype.getValue = function(r){
+ var ret = {};
+ for(var p in this._properties){
+ var prop = this._properties[p],
+ start = prop.start;
+ if(start instanceof Color){
+ ret[p] = Color.blendColors(start, prop.end, r, prop.tempColor).toCss();
+ }else if(!lang.isArray(start)){
+ ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units || "px" : 0);
+ }
+ }
+ return ret;
+ };
+
+ /*=====
+ var __AnimArgs = declare(__FadeArgs, {
+ // properties: Object?
+ // A hash map of style properties to Objects describing the transition,
+ // such as the properties of _Line with an additional 'units' property
+ properties: {}
+
+ //TODOC: add event callbacks
+ });
+ =====*/
+
+ basefx.animateProperty = function(/*__AnimArgs*/ args){
+ // summary:
+ // Returns an animation that will transition the properties of
+ // node defined in `args` depending how they are defined in
+ // `args.properties`
+ //
+ // description:
+ // Foundation of most `dojo/_base/fx`
+ // animations. It takes an object of "properties" corresponding to
+ // style properties, and animates them in parallel over a set
+ // duration.
+ //
+ // example:
+ // A simple animation that changes the width of the specified node.
+ // | basefx.animateProperty({
+ // | node: "nodeId",
+ // | properties: { width: 400 },
+ // | }).play();
+ // Dojo figures out the start value for the width and converts the
+ // integer specified for the width to the more expressive but
+ // verbose form `{ width: { end: '400', units: 'px' } }` which you
+ // can also specify directly. Defaults to 'px' if omitted.
+ //
+ // example:
+ // Animate width, height, and padding over 2 seconds... the
+ // pedantic way:
+ // | basefx.animateProperty({ node: node, duration:2000,
+ // | properties: {
+ // | width: { start: '200', end: '400', units:"px" },
+ // | height: { start:'200', end: '400', units:"px" },
+ // | paddingTop: { start:'5', end:'50', units:"px" }
+ // | }
+ // | }).play();
+ // Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties
+ // are written using "mixed case", as the hyphen is illegal as an object key.
+ //
+ // example:
+ // Plug in a different easing function and register a callback for
+ // when the animation ends. Easing functions accept values between
+ // zero and one and return a value on that basis. In this case, an
+ // exponential-in curve.
+ // | basefx.animateProperty({
+ // | node: "nodeId",
+ // | // dojo figures out the start value
+ // | properties: { width: { end: 400 } },
+ // | easing: function(n){
+ // | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
+ // | },
+ // | onEnd: function(node){
+ // | // called when the animation finishes. The animation
+ // | // target is passed to this function
+ // | }
+ // | }).play(500); // delay playing half a second
+ //
+ // example:
+ // Like all `Animation`s, animateProperty returns a handle to the
+ // Animation instance, which fires the events common to Dojo FX. Use `aspect.after`
+ // to access these events outside of the Animation definition:
+ // | var anim = basefx.animateProperty({
+ // | node:"someId",
+ // | properties:{
+ // | width:400, height:500
+ // | }
+ // | });
+ // | aspect.after(anim, "onEnd", function(){
+ // | console.log("animation ended");
+ // | }, true);
+ // | // play the animation now:
+ // | anim.play();
+ //
+ // example:
+ // Each property can be a function whose return value is substituted along.
+ // Additionally, each measurement (eg: start, end) can be a function. The node
+ // reference is passed directly to callbacks.
+ // | basefx.animateProperty({
+ // | node:"mine",
+ // | properties:{
+ // | height:function(node){
+ // | // shrink this node by 50%
+ // | return domGeom.position(node).h / 2
+ // | },
+ // | width:{
+ // | start:function(node){ return 100; },
+ // | end:function(node){ return 200; }
+ // | }
+ // | }
+ // | }).play();
+ //
+
+ var n = args.node = dom.byId(args.node);
+ if(!args.easing){ args.easing = dojo._defaultEasing; }
+
+ var anim = new Animation(args);
+ aspect.after(anim, "beforeBegin", lang.hitch(anim, function(){
+ var pm = {};
+ for(var p in this.properties){
+ // Make shallow copy of properties into pm because we overwrite
+ // some values below. In particular if start/end are functions
+ // we don't want to overwrite them or the functions won't be
+ // called if the animation is reused.
+ if(p == "width" || p == "height"){
+ this.node.display = "block";
+ }
+ var prop = this.properties[p];
+ if(lang.isFunction(prop)){
+ prop = prop(n);
+ }
+ prop = pm[p] = _mixin({}, (lang.isObject(prop) ? prop: { end: prop }));
+
+ if(lang.isFunction(prop.start)){
+ prop.start = prop.start(n);
+ }
+ if(lang.isFunction(prop.end)){
+ prop.end = prop.end(n);
+ }
+ var isColor = (p.toLowerCase().indexOf("color") >= 0);
+ function getStyle(node, p){
+ // domStyle.get(node, "height") can return "auto" or "" on IE; this is more reliable:
+ var v = { height: node.offsetHeight, width: node.offsetWidth }[p];
+ if(v !== undefined){ return v; }
+ v = style.get(node, p);
+ return (p == "opacity") ? +v : (isColor ? v : parseFloat(v));
+ }
+ if(!("end" in prop)){
+ prop.end = getStyle(n, p);
+ }else if(!("start" in prop)){
+ prop.start = getStyle(n, p);
+ }
+
+ if(isColor){
+ prop.start = new Color(prop.start);
+ prop.end = new Color(prop.end);
+ }else{
+ prop.start = (p == "opacity") ? +prop.start : parseFloat(prop.start);
+ }
+ }
+ this.curve = new PropLine(pm);
+ }), true);
+ aspect.after(anim, "onAnimate", lang.hitch(style, "set", anim.node), true);
+ return anim; // Animation
+ };
+
+ basefx.anim = function( /*DOMNode|String*/ node,
+ /*Object*/ properties,
+ /*Integer?*/ duration,
+ /*Function?*/ easing,
+ /*Function?*/ onEnd,
+ /*Integer?*/ delay){
+ // summary:
+ // A simpler interface to `animateProperty()`, also returns
+ // an instance of `Animation` but begins the animation
+ // immediately, unlike nearly every other Dojo animation API.
+ // description:
+ // Simpler (but somewhat less powerful) version
+ // of `animateProperty`. It uses defaults for many basic properties
+ // and allows for positional parameters to be used in place of the
+ // packed "property bag" which is used for other Dojo animation
+ // methods.
+ //
+ // The `Animation` object returned will be already playing, so
+ // calling play() on it again is (usually) a no-op.
+ // node:
+ // a DOM node or the id of a node to animate CSS properties on
+ // duration:
+ // The number of milliseconds over which the animation
+ // should run. Defaults to the global animation default duration
+ // (350ms).
+ // easing:
+ // An easing function over which to calculate acceleration
+ // and deceleration of the animation through its duration.
+ // A default easing algorithm is provided, but you may
+ // plug in any you wish. A large selection of easing algorithms
+ // are available in `dojo/fx/easing`.
+ // onEnd:
+ // A function to be called when the animation finishes
+ // running.
+ // delay:
+ // The number of milliseconds to delay beginning the
+ // animation by. The default is 0.
+ // example:
+ // Fade out a node
+ // | basefx.anim("id", { opacity: 0 });
+ // example:
+ // Fade out a node over a full second
+ // | basefx.anim("id", { opacity: 0 }, 1000);
+ return basefx.animateProperty({ // Animation
+ node: node,
+ duration: duration || Animation.prototype.duration,
+ properties: properties,
+ easing: easing,
+ onEnd: onEnd
+ }).play(delay || 0);
+ };
+
+
+ if(has("extend-dojo")){
+ _mixin(dojo, basefx);
+ // Alias to drop come 2.0:
+ dojo._Animation = Animation;
+ }
+
+ return basefx;
+});
diff --git a/src/main/resources/static/dojo/_base/html.js b/src/main/resources/static/dojo/_base/html.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6a703a8da11faacbd3da8a3fff5553ca3a6a6d0
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/html.js
@@ -0,0 +1,392 @@
+define(["./kernel", "../dom", "../dom-style", "../dom-attr", "../dom-prop", "../dom-class", "../dom-construct", "../dom-geometry"], function(dojo, dom, style, attr, prop, cls, ctr, geom){
+ // module:
+ // dojo/dom
+
+ /*=====
+ return {
+ // summary:
+ // This module is a stub for the core dojo DOM API.
+ };
+ =====*/
+
+ // mix-in dom
+ dojo.byId = dom.byId;
+ dojo.isDescendant = dom.isDescendant;
+ dojo.setSelectable = dom.setSelectable;
+
+ // mix-in dom-attr
+ dojo.getAttr = attr.get;
+ dojo.setAttr = attr.set;
+ dojo.hasAttr = attr.has;
+ dojo.removeAttr = attr.remove;
+ dojo.getNodeProp = attr.getNodeProp;
+
+ dojo.attr = function(node, name, value){
+ // summary:
+ // Gets or sets an attribute on an HTML element.
+ // description:
+ // Handles normalized getting and setting of attributes on DOM
+ // Nodes. If 2 arguments are passed, and a the second argument is a
+ // string, acts as a getter.
+ //
+ // If a third argument is passed, or if the second argument is a
+ // map of attributes, acts as a setter.
+ //
+ // When passing functions as values, note that they will not be
+ // directly assigned to slots on the node, but rather the default
+ // behavior will be removed and the new behavior will be added
+ // using `dojo.connect()`, meaning that event handler properties
+ // will be normalized and that some caveats with regards to
+ // non-standard behaviors for onsubmit apply. Namely that you
+ // should cancel form submission using `dojo.stopEvent()` on the
+ // passed event object instead of returning a boolean value from
+ // the handler itself.
+ // node: DOMNode|String
+ // id or reference to the element to get or set the attribute on
+ // name: String|Object
+ // the name of the attribute to get or set.
+ // value: String?
+ // The value to set for the attribute
+ // returns:
+ // when used as a getter, the value of the requested attribute
+ // or null if that attribute does not have a specified or
+ // default value;
+ //
+ // when used as a setter, the DOM node
+ //
+ // example:
+ // | // get the current value of the "foo" attribute on a node
+ // | dojo.attr(dojo.byId("nodeId"), "foo");
+ // | // or we can just pass the id:
+ // | dojo.attr("nodeId", "foo");
+ //
+ // example:
+ // | // use attr() to set the tab index
+ // | dojo.attr("nodeId", "tabIndex", 3);
+ // |
+ //
+ // example:
+ // Set multiple values at once, including event handlers:
+ // | dojo.attr("formId", {
+ // | "foo": "bar",
+ // | "tabIndex": -1,
+ // | "method": "POST",
+ // | "onsubmit": function(e){
+ // | // stop submitting the form. Note that the IE behavior
+ // | // of returning true or false will have no effect here
+ // | // since our handler is connect()ed to the built-in
+ // | // onsubmit behavior and so we need to use
+ // | // dojo.stopEvent() to ensure that the submission
+ // | // doesn't proceed.
+ // | dojo.stopEvent(e);
+ // |
+ // | // submit the form with Ajax
+ // | dojo.xhrPost({ form: "formId" });
+ // | }
+ // | });
+ //
+ // example:
+ // Style is s special case: Only set with an object hash of styles
+ // | dojo.attr("someNode",{
+ // | id:"bar",
+ // | style:{
+ // | width:"200px", height:"100px", color:"#000"
+ // | }
+ // | });
+ //
+ // example:
+ // Again, only set style as an object hash of styles:
+ // | var obj = { color:"#fff", backgroundColor:"#000" };
+ // | dojo.attr("someNode", "style", obj);
+ // |
+ // | // though shorter to use `dojo.style()` in this case:
+ // | dojo.style("someNode", obj);
+
+ if(arguments.length == 2){
+ return attr[typeof name == "string" ? "get" : "set"](node, name);
+ }
+ return attr.set(node, name, value);
+ };
+
+ // mix-in dom-class
+ dojo.hasClass = cls.contains;
+ dojo.addClass = cls.add;
+ dojo.removeClass = cls.remove;
+ dojo.toggleClass = cls.toggle;
+ dojo.replaceClass = cls.replace;
+
+ // mix-in dom-construct
+ dojo._toDom = dojo.toDom = ctr.toDom;
+ dojo.place = ctr.place;
+ dojo.create = ctr.create;
+ dojo.empty = function(node){ ctr.empty(node); };
+ dojo._destroyElement = dojo.destroy = function(node){ ctr.destroy(node); };
+
+ // mix-in dom-geometry
+ dojo._getPadExtents = dojo.getPadExtents = geom.getPadExtents;
+ dojo._getBorderExtents = dojo.getBorderExtents = geom.getBorderExtents;
+ dojo._getPadBorderExtents = dojo.getPadBorderExtents = geom.getPadBorderExtents;
+ dojo._getMarginExtents = dojo.getMarginExtents = geom.getMarginExtents;
+ dojo._getMarginSize = dojo.getMarginSize = geom.getMarginSize;
+ dojo._getMarginBox = dojo.getMarginBox = geom.getMarginBox;
+ dojo.setMarginBox = geom.setMarginBox;
+ dojo._getContentBox = dojo.getContentBox = geom.getContentBox;
+ dojo.setContentSize = geom.setContentSize;
+ dojo._isBodyLtr = dojo.isBodyLtr = geom.isBodyLtr;
+ dojo._docScroll = dojo.docScroll = geom.docScroll;
+ dojo._getIeDocumentElementOffset = dojo.getIeDocumentElementOffset = geom.getIeDocumentElementOffset;
+ dojo._fixIeBiDiScrollLeft = dojo.fixIeBiDiScrollLeft = geom.fixIeBiDiScrollLeft;
+ dojo.position = geom.position;
+
+ dojo.marginBox = function marginBox(/*DomNode|String*/node, /*Object?*/box){
+ // summary:
+ // Getter/setter for the margin-box of node.
+ // description:
+ // Getter/setter for the margin-box of node.
+ // Returns an object in the expected format of box (regardless
+ // if box is passed). The object might look like:
+ // `{ l: 50, t: 200, w: 300: h: 150 }`
+ // for a node offset from its parent 50px to the left, 200px from
+ // the top with a margin width of 300px and a margin-height of
+ // 150px.
+ // node:
+ // id or reference to DOM Node to get/set box for
+ // box:
+ // If passed, denotes that dojo.marginBox() should
+ // update/set the margin box for node. Box is an object in the
+ // above format. All properties are optional if passed.
+ // example:
+ // Retrieve the margin box of a passed node
+ // | var box = dojo.marginBox("someNodeId");
+ // | console.dir(box);
+ //
+ // example:
+ // Set a node's margin box to the size of another node
+ // | var box = dojo.marginBox("someNodeId");
+ // | dojo.marginBox("someOtherNode", box);
+ return box ? geom.setMarginBox(node, box) : geom.getMarginBox(node); // Object
+ };
+
+ dojo.contentBox = function contentBox(/*DomNode|String*/node, /*Object?*/box){
+ // summary:
+ // Getter/setter for the content-box of node.
+ // description:
+ // Returns an object in the expected format of box (regardless if box is passed).
+ // The object might look like:
+ // `{ l: 50, t: 200, w: 300: h: 150 }`
+ // for a node offset from its parent 50px to the left, 200px from
+ // the top with a content width of 300px and a content-height of
+ // 150px. Note that the content box may have a much larger border
+ // or margin box, depending on the box model currently in use and
+ // CSS values set/inherited for node.
+ // While the getter will return top and left values, the
+ // setter only accepts setting the width and height.
+ // node:
+ // id or reference to DOM Node to get/set box for
+ // box:
+ // If passed, denotes that dojo.contentBox() should
+ // update/set the content box for node. Box is an object in the
+ // above format, but only w (width) and h (height) are supported.
+ // All properties are optional if passed.
+ return box ? geom.setContentSize(node, box) : geom.getContentBox(node); // Object
+ };
+
+ dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
+ // summary:
+ // Deprecated: Use position() for border-box x/y/w/h
+ // or marginBox() for margin-box w/h/l/t.
+ //
+ // Returns an object that measures margin-box (w)idth/(h)eight
+ // and absolute position x/y of the border-box. Also returned
+ // is computed (l)eft and (t)op values in pixels from the
+ // node's offsetParent as returned from marginBox().
+ // Return value will be in the form:
+ //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }
+ // Does not act as a setter. If includeScroll is passed, the x and
+ // y params are affected as one would expect in dojo.position().
+ dojo.deprecated("dojo.coords()", "Use dojo.position() or dojo.marginBox().");
+ node = dom.byId(node);
+ var s = style.getComputedStyle(node), mb = geom.getMarginBox(node, s);
+ var abs = geom.position(node, includeScroll);
+ mb.x = abs.x;
+ mb.y = abs.y;
+ return mb; // Object
+ };
+
+ // mix-in dom-prop
+ dojo.getProp = prop.get;
+ dojo.setProp = prop.set;
+
+ dojo.prop = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
+ // summary:
+ // Gets or sets a property on an HTML element.
+ // description:
+ // Handles normalized getting and setting of properties on DOM
+ // Nodes. If 2 arguments are passed, and a the second argument is a
+ // string, acts as a getter.
+ //
+ // If a third argument is passed, or if the second argument is a
+ // map of attributes, acts as a setter.
+ //
+ // When passing functions as values, note that they will not be
+ // directly assigned to slots on the node, but rather the default
+ // behavior will be removed and the new behavior will be added
+ // using `dojo.connect()`, meaning that event handler properties
+ // will be normalized and that some caveats with regards to
+ // non-standard behaviors for onsubmit apply. Namely that you
+ // should cancel form submission using `dojo.stopEvent()` on the
+ // passed event object instead of returning a boolean value from
+ // the handler itself.
+ // node:
+ // id or reference to the element to get or set the property on
+ // name:
+ // the name of the property to get or set.
+ // value:
+ // The value to set for the property
+ // returns:
+ // when used as a getter, the value of the requested property
+ // or null if that attribute does not have a specified or
+ // default value;
+ //
+ // when used as a setter, the DOM node
+ //
+ // example:
+ // | // get the current value of the "foo" property on a node
+ // | dojo.prop(dojo.byId("nodeId"), "foo");
+ // | // or we can just pass the id:
+ // | dojo.prop("nodeId", "foo");
+ //
+ // example:
+ // | // use prop() to set the tab index
+ // | dojo.prop("nodeId", "tabIndex", 3);
+ // |
+ //
+ // example:
+ // Set multiple values at once, including event handlers:
+ // | dojo.prop("formId", {
+ // | "foo": "bar",
+ // | "tabIndex": -1,
+ // | "method": "POST",
+ // | "onsubmit": function(e){
+ // | // stop submitting the form. Note that the IE behavior
+ // | // of returning true or false will have no effect here
+ // | // since our handler is connect()ed to the built-in
+ // | // onsubmit behavior and so we need to use
+ // | // dojo.stopEvent() to ensure that the submission
+ // | // doesn't proceed.
+ // | dojo.stopEvent(e);
+ // |
+ // | // submit the form with Ajax
+ // | dojo.xhrPost({ form: "formId" });
+ // | }
+ // | });
+ //
+ // example:
+ // Style is s special case: Only set with an object hash of styles
+ // | dojo.prop("someNode",{
+ // | id:"bar",
+ // | style:{
+ // | width:"200px", height:"100px", color:"#000"
+ // | }
+ // | });
+ //
+ // example:
+ // Again, only set style as an object hash of styles:
+ // | var obj = { color:"#fff", backgroundColor:"#000" };
+ // | dojo.prop("someNode", "style", obj);
+ // |
+ // | // though shorter to use `dojo.style()` in this case:
+ // | dojo.style("someNode", obj);
+
+ if(arguments.length == 2){
+ return prop[typeof name == "string" ? "get" : "set"](node, name);
+ }
+ // setter
+ return prop.set(node, name, value);
+ };
+
+ // mix-in dom-style
+ dojo.getStyle = style.get;
+ dojo.setStyle = style.set;
+ dojo.getComputedStyle = style.getComputedStyle;
+ dojo.__toPixelValue = dojo.toPixelValue = style.toPixelValue;
+
+ dojo.style = function(node, name, value){
+ // summary:
+ // Accesses styles on a node. If 2 arguments are
+ // passed, acts as a getter. If 3 arguments are passed, acts
+ // as a setter.
+ // description:
+ // Getting the style value uses the computed style for the node, so the value
+ // will be a calculated value, not just the immediate node.style value.
+ // Also when getting values, use specific style names,
+ // like "borderBottomWidth" instead of "border" since compound values like
+ // "border" are not necessarily reflected as expected.
+ // If you want to get node dimensions, use `dojo.marginBox()`,
+ // `dojo.contentBox()` or `dojo.position()`.
+ // node: DOMNode|String
+ // id or reference to node to get/set style for
+ // name: String|Object?
+ // the style property to set in DOM-accessor format
+ // ("borderWidth", not "border-width") or an object with key/value
+ // pairs suitable for setting each property.
+ // value: String?
+ // If passed, sets value on the node for style, handling
+ // cross-browser concerns. When setting a pixel value,
+ // be sure to include "px" in the value. For instance, top: "200px".
+ // Otherwise, in some cases, some browsers will not apply the style.
+ // returns:
+ // when used as a getter, return the computed style of the node if passing in an ID or node,
+ // or return the normalized, computed value for the property when passing in a node and a style property
+ // example:
+ // Passing only an ID or node returns the computed style object of
+ // the node:
+ // | dojo.style("thinger");
+ // example:
+ // Passing a node and a style property returns the current
+ // normalized, computed value for that property:
+ // | dojo.style("thinger", "opacity"); // 1 by default
+ //
+ // example:
+ // Passing a node, a style property, and a value changes the
+ // current display of the node and returns the new computed value
+ // | dojo.style("thinger", "opacity", 0.5); // == 0.5
+ //
+ // example:
+ // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
+ // | dojo.style("thinger", {
+ // | "opacity": 0.5,
+ // | "border": "3px solid black",
+ // | "height": "300px"
+ // | });
+ //
+ // example:
+ // When the CSS style property is hyphenated, the JavaScript property is camelCased.
+ // font-size becomes fontSize, and so on.
+ // | dojo.style("thinger",{
+ // | fontSize:"14pt",
+ // | letterSpacing:"1.2em"
+ // | });
+ //
+ // example:
+ // dojo/NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
+ // dojo.style() on every element of the list. See: `dojo/query` and `dojo/NodeList`
+ // | dojo.query(".someClassName").style("visibility","hidden");
+ // | // or
+ // | dojo.query("#baz > div").style({
+ // | opacity:0.75,
+ // | fontSize:"13pt"
+ // | });
+
+ switch(arguments.length){
+ case 1:
+ return style.get(node);
+ case 2:
+ return style[typeof name == "string" ? "get" : "set"](node, name);
+ }
+ // setter
+ return style.set(node, name, value);
+ };
+
+ return dojo;
+});
diff --git a/src/main/resources/static/dojo/_base/json.js b/src/main/resources/static/dojo/_base/json.js
new file mode 100644
index 0000000000000000000000000000000000000000..176ae36648502d2692cccf9bcfd1aa0d2463f42e
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/json.js
@@ -0,0 +1,91 @@
+define(["./kernel", "../json"], function(dojo, json){
+
+// module:
+// dojo/_base/json
+
+/*=====
+return {
+ // summary:
+ // This module defines the dojo JSON API.
+};
+=====*/
+
+dojo.fromJson = function(/*String*/ js){
+ // summary:
+ // Parses a JavaScript expression and returns a JavaScript value.
+ // description:
+ // Throws for invalid JavaScript expressions. It does not use a strict JSON parser. It
+ // always delegates to eval(). The content passed to this method must therefore come
+ // from a trusted source.
+ // It is recommend that you use dojo/json's parse function for an
+ // implementation uses the (faster) native JSON parse when available.
+ // js:
+ // a string literal of a JavaScript expression, for instance:
+ // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
+
+ return eval("(" + js + ")"); // Object
+};
+
+/*=====
+dojo._escapeString = function(){
+ // summary:
+ // Adds escape sequences for non-visual characters, double quote and
+ // backslash and surrounds with double quotes to form a valid string
+ // literal.
+};
+=====*/
+dojo._escapeString = json.stringify; // just delegate to json.stringify
+
+dojo.toJsonIndentStr = "\t";
+dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint){
+ // summary:
+ // Returns a [JSON](http://json.org) serialization of an object.
+ // description:
+ // Returns a [JSON](http://json.org) serialization of an object.
+ // Note that this doesn't check for infinite recursion, so don't do that!
+ // It is recommend that you use dojo/json's stringify function for an lighter
+ // and faster implementation that matches the native JSON API and uses the
+ // native JSON serializer when available.
+ // it:
+ // an object to be serialized. Objects may define their own
+ // serialization via a special "__json__" or "json" function
+ // property. If a specialized serializer has been defined, it will
+ // be used as a fallback.
+ // Note that in 1.6, toJson would serialize undefined, but this no longer supported
+ // since it is not supported by native JSON serializer.
+ // prettyPrint:
+ // if true, we indent objects and arrays to make the output prettier.
+ // The variable `dojo.toJsonIndentStr` is used as the indent string --
+ // to use something other than the default (tab), change that variable
+ // before calling dojo.toJson().
+ // Note that if native JSON support is available, it will be used for serialization,
+ // and native implementations vary on the exact spacing used in pretty printing.
+ // returns:
+ // A JSON string serialization of the passed-in object.
+ // example:
+ // simple serialization of a trivial object
+ // | var jsonStr = dojo.toJson({ howdy: "stranger!", isStrange: true });
+ // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr);
+ // example:
+ // a custom serializer for an objects of a particular class:
+ // | dojo.declare("Furby", null, {
+ // | furbies: "are strange",
+ // | furbyCount: 10,
+ // | __json__: function(){
+ // | },
+ // | });
+
+ // use dojo/json
+ return json.stringify(it, function(key, value){
+ if(value){
+ var tf = value.__json__||value.json;
+ if(typeof tf == "function"){
+ return tf.call(value);
+ }
+ }
+ return value;
+ }, prettyPrint && dojo.toJsonIndentStr); // String
+};
+
+return dojo;
+});
diff --git a/src/main/resources/static/dojo/_base/kernel.js b/src/main/resources/static/dojo/_base/kernel.js
new file mode 100644
index 0000000000000000000000000000000000000000..43d15202297358e8fe52df03ed4ea829c02c6d42
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/kernel.js
@@ -0,0 +1,299 @@
+define(["../has", "./config", "require", "module"], function(has, config, require, module){
+ // module:
+ // dojo/_base/kernel
+
+ // This module is the foundational module of the dojo boot sequence; it defines the dojo object.
+
+ var
+ // loop variables for this module
+ i, p,
+
+ // create dojo, dijit, and dojox
+ // FIXME: in 2.0 remove dijit, dojox being created by dojo
+ dijit = {},
+ dojox = {},
+ dojo = {
+ // summary:
+ // This module is the foundational module of the dojo boot sequence; it defines the dojo object.
+
+ // notice dojo takes ownership of the value of the config module
+ config:config,
+ global:this,
+ dijit:dijit,
+ dojox:dojox
+ };
+
+
+ // Configure the scope map. For a 100% AMD application, the scope map is not needed other than to provide
+ // a _scopeName property for the dojo, dijit, and dojox root object so those packages can create
+ // unique names in the global space.
+ //
+ // Built, legacy modules use the scope map to allow those modules to be expressed as if dojo, dijit, and dojox,
+ // where global when in fact they are either global under different names or not global at all. In v1.6-, the
+ // config variable "scopeMap" was used to map names as used within a module to global names. This has been
+ // subsumed by the AMD map configuration variable which can relocate packages to different names. For backcompat,
+ // only the "*" mapping is supported. See http://livedocs.dojotoolkit.org/developer/design/loader#legacy-cross-domain-mode for details.
+ //
+ // The following computations contort the packageMap for this dojo instance into a scopeMap.
+ var scopeMap =
+ // a map from a name used in a legacy module to the (global variable name, object addressed by that name)
+ // always map dojo, dijit, and dojox
+ {
+ dojo:["dojo", dojo],
+ dijit:["dijit", dijit],
+ dojox:["dojox", dojox]
+ },
+
+ packageMap =
+ // the package map for this dojo instance; note, a foreign loader or no pacakgeMap results in the above default config
+ (require.map && require.map[module.id.match(/[^\/]+/)[0]]),
+
+ item;
+
+
+ // process all mapped top-level names for this instance of dojo
+ for(p in packageMap){
+ if(scopeMap[p]){
+ // mapped dojo, dijit, or dojox
+ scopeMap[p][0] = packageMap[p];
+ }else{
+ // some other top-level name
+ scopeMap[p] = [packageMap[p], {}];
+ }
+ }
+
+ // publish those names to _scopeName and, optionally, the global namespace
+ for(p in scopeMap){
+ item = scopeMap[p];
+ item[1]._scopeName = item[0];
+ if(!config.noGlobals){
+ this[item[0]] = item[1];
+ }
+ }
+ dojo.scopeMap = scopeMap;
+
+ /*===== dojo.__docParserConfigureScopeMap(scopeMap); =====*/
+
+ // FIXME: dojo.baseUrl and dojo.config.baseUrl should be deprecated
+ dojo.baseUrl = dojo.config.baseUrl = require.baseUrl;
+ dojo.isAsync = !has("dojo-loader") || require.async;
+ dojo.locale = config.locale;
+
+ var rev = "$Rev: 43d05c6 $".match(/\d+/);
+ dojo.version = {
+ // summary:
+ // Version number of the Dojo Toolkit
+ // description:
+ // Hash about the version, including
+ //
+ // - major: Integer: Major version. If total version is "1.2.0beta1", will be 1
+ // - minor: Integer: Minor version. If total version is "1.2.0beta1", will be 2
+ // - patch: Integer: Patch version. If total version is "1.2.0beta1", will be 0
+ // - flag: String: Descriptor flag. If total version is "1.2.0beta1", will be "beta1"
+ // - revision: Number: The SVN rev from which dojo was pulled
+
+ major: 1, minor: 9, patch: 1, flag: "",
+ revision: rev ? +rev[0] : NaN,
+ toString: function(){
+ var v = dojo.version;
+ return v.major + "." + v.minor + "." + v.patch + v.flag + " (" + v.revision + ")"; // String
+ }
+ };
+
+ // If has("extend-dojo") is truthy, then as a dojo module is defined it should push it's definitions
+ // into the dojo object, and conversely. In 2.0, it will likely be unusual to augment another object
+ // as a result of defining a module. This has feature gives a way to force 2.0 behavior as the code
+ // is migrated. Absent specific advice otherwise, set extend-dojo to truthy.
+ has.add("extend-dojo", 1);
+
+
+ (Function("d", "d.eval = function(){return d.global.eval ? d.global.eval(arguments[0]) : eval(arguments[0]);}"))(dojo);
+ /*=====
+ dojo.eval = function(scriptText){
+ // summary:
+ // A legacy method created for use exclusively by internal Dojo methods. Do not use this method
+ // directly unless you understand its possibly-different implications on the platforms your are targeting.
+ // description:
+ // Makes an attempt to evaluate scriptText in the global scope. The function works correctly for browsers
+ // that support indirect eval.
+ //
+ // As usual, IE does not. On IE, the only way to implement global eval is to
+ // use execScript. Unfortunately, execScript does not return a value and breaks some current usages of dojo.eval.
+ // This implementation uses the technique of executing eval in the scope of a function that is a single scope
+ // frame below the global scope; thereby coming close to the global scope. Note carefully that
+ //
+ // dojo.eval("var pi = 3.14;");
+ //
+ // will define global pi in non-IE environments, but define pi only in a temporary local scope for IE. If you want
+ // to define a global variable using dojo.eval, write something like
+ //
+ // dojo.eval("window.pi = 3.14;")
+ // scriptText:
+ // The text to evaluation.
+ // returns:
+ // The result of the evaluation. Often `undefined`
+ };
+ =====*/
+
+
+ if(has("host-rhino")){
+ dojo.exit = function(exitcode){
+ quit(exitcode);
+ };
+ }else{
+ dojo.exit = function(){
+ };
+ }
+
+ has.add("dojo-guarantee-console",
+ // ensure that console.log, console.warn, etc. are defined
+ 1
+ );
+ if(has("dojo-guarantee-console")){
+ typeof console != "undefined" || (console = {});
+ // Be careful to leave 'log' always at the end
+ var cn = [
+ "assert", "count", "debug", "dir", "dirxml", "error", "group",
+ "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd",
+ "trace", "warn", "log"
+ ];
+ var tn;
+ i = 0;
+ while((tn = cn[i++])){
+ if(!console[tn]){
+ (function(){
+ var tcn = tn + "";
+ console[tcn] = ('log' in console) ? function(){
+ var a = Array.apply({}, arguments);
+ a.unshift(tcn + ":");
+ console["log"](a.join(" "));
+ } : function(){};
+ console[tcn]._fake = true;
+ })();
+ }
+ }
+ }
+
+ has.add("dojo-debug-messages",
+ // include dojo.deprecated/dojo.experimental implementations
+ !!config.isDebug
+ );
+ dojo.deprecated = dojo.experimental = function(){};
+ if(has("dojo-debug-messages")){
+ dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
+ // summary:
+ // Log a debug message to indicate that a behavior has been
+ // deprecated.
+ // behaviour: String
+ // The API or behavior being deprecated. Usually in the form
+ // of "myApp.someFunction()".
+ // extra: String?
+ // Text to append to the message. Often provides advice on a
+ // new function or facility to achieve the same goal during
+ // the deprecation period.
+ // removal: String?
+ // Text to indicate when in the future the behavior will be
+ // removed. Usually a version number.
+ // example:
+ // | dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0");
+
+ var message = "DEPRECATED: " + behaviour;
+ if(extra){ message += " " + extra; }
+ if(removal){ message += " -- will be removed in version: " + removal; }
+ console.warn(message);
+ };
+
+ dojo.experimental = function(/* String */ moduleName, /* String? */ extra){
+ // summary:
+ // Marks code as experimental.
+ // description:
+ // This can be used to mark a function, file, or module as
+ // experimental. Experimental code is not ready to be used, and the
+ // APIs are subject to change without notice. Experimental code may be
+ // completed deleted without going through the normal deprecation
+ // process.
+ // moduleName: String
+ // The name of a module, or the name of a module file or a specific
+ // function
+ // extra: String?
+ // some additional message for the user
+ // example:
+ // | dojo.experimental("dojo.data.Result");
+ // example:
+ // | dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");
+
+ var message = "EXPERIMENTAL: " + moduleName + " -- APIs subject to change without notice.";
+ if(extra){ message += " " + extra; }
+ console.warn(message);
+ };
+ }
+
+ has.add("dojo-modulePaths",
+ // consume dojo.modulePaths processing
+ 1
+ );
+ if(has("dojo-modulePaths")){
+ // notice that modulePaths won't be applied to any require's before the dojo/_base/kernel factory is run;
+ // this is the v1.6- behavior.
+ if(config.modulePaths){
+ dojo.deprecated("dojo.modulePaths", "use paths configuration");
+ var paths = {};
+ for(p in config.modulePaths){
+ paths[p.replace(/\./g, "/")] = config.modulePaths[p];
+ }
+ require({paths:paths});
+ }
+ }
+
+ has.add("dojo-moduleUrl",
+ // include dojo.moduleUrl
+ 1
+ );
+ if(has("dojo-moduleUrl")){
+ dojo.moduleUrl = function(/*String*/module, /*String?*/url){
+ // summary:
+ // Returns a URL relative to a module.
+ // example:
+ // | var pngPath = dojo.moduleUrl("acme","images/small.png");
+ // | console.dir(pngPath); // list the object properties
+ // | // create an image and set it's source to pngPath's value:
+ // | var img = document.createElement("img");
+ // | img.src = pngPath;
+ // | // add our image to the document
+ // | dojo.body().appendChild(img);
+ // example:
+ // you may de-reference as far as you like down the package
+ // hierarchy. This is sometimes handy to avoid lenghty relative
+ // urls or for building portable sub-packages. In this example,
+ // the `acme.widget` and `acme.util` directories may be located
+ // under different roots (see `dojo.registerModulePath`) but the
+ // the modules which reference them can be unaware of their
+ // relative locations on the filesystem:
+ // | // somewhere in a configuration block
+ // | dojo.registerModulePath("acme.widget", "../../acme/widget");
+ // | dojo.registerModulePath("acme.util", "../../util");
+ // |
+ // | // ...
+ // |
+ // | // code in a module using acme resources
+ // | var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html");
+ // | var dataPath = dojo.moduleUrl("acme.util","resources/data.json");
+
+ dojo.deprecated("dojo.moduleUrl()", "use require.toUrl", "2.0");
+
+ // require.toUrl requires a filetype; therefore, just append the suffix "/*.*" to guarantee a filetype, then
+ // remove the suffix from the result. This way clients can request a url w/out a filetype. This should be
+ // rare, but it maintains backcompat for the v1.x line (note: dojo.moduleUrl will be removed in v2.0).
+ // Notice * is an illegal filename so it won't conflict with any real path map that may exist the paths config.
+ var result = null;
+ if(module){
+ result = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : "") + "/*.*").replace(/\/\*\.\*/, "") + (url ? "" : "/");
+ }
+ return result;
+ };
+ }
+
+ dojo._hasResource = {}; // for backward compatibility with layers built with 1.6 tooling
+
+ return dojo;
+});
diff --git a/src/main/resources/static/dojo/_base/lang.js b/src/main/resources/static/dojo/_base/lang.js
new file mode 100644
index 0000000000000000000000000000000000000000..48a9c43c72cd3eb7c84c843f76f25941178d0ce7
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/lang.js
@@ -0,0 +1,605 @@
+define(["./kernel", "../has", "../sniff"], function(dojo, has){
+ // module:
+ // dojo/_base/lang
+
+ has.add("bug-for-in-skips-shadowed", function(){
+ // if true, the for-in iterator skips object properties that exist in Object's prototype (IE 6 - ?)
+ for(var i in {toString: 1}){
+ return 0;
+ }
+ return 1;
+ });
+
+ // Helper methods
+ var _extraNames =
+ has("bug-for-in-skips-shadowed") ?
+ "hasOwnProperty.valueOf.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.constructor".split(".") : [],
+
+ _extraLen = _extraNames.length,
+
+ getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){
+ var p, i = 0, dojoGlobal = dojo.global;
+ if(!context){
+ if(!parts.length){
+ return dojoGlobal;
+ }else{
+ p = parts[i++];
+ try{
+ context = dojo.scopeMap[p] && dojo.scopeMap[p][1];
+ }catch(e){}
+ context = context || (p in dojoGlobal ? dojoGlobal[p] : (create ? dojoGlobal[p] = {} : undefined));
+ }
+ }
+ while(context && (p = parts[i++])){
+ context = (p in context ? context[p] : (create ? context[p] = {} : undefined));
+ }
+ return context; // mixed
+ },
+
+ opts = Object.prototype.toString,
+
+ efficient = function(obj, offset, startWith){
+ return (startWith||[]).concat(Array.prototype.slice.call(obj, offset||0));
+ },
+
+ _pattern = /\{([^\}]+)\}/g;
+
+ // Module export
+ var lang = {
+ // summary:
+ // This module defines Javascript language extensions.
+
+ // _extraNames: String[]
+ // Lists property names that must be explicitly processed during for-in iteration
+ // in environments that have has("bug-for-in-skips-shadowed") true.
+ _extraNames:_extraNames,
+
+ _mixin: function(dest, source, copyFunc){
+ // summary:
+ // Copies/adds all properties of source to dest; returns dest.
+ // dest: Object
+ // The object to which to copy/add all properties contained in source.
+ // source: Object
+ // The object from which to draw all properties to copy into dest.
+ // copyFunc: Function?
+ // The process used to copy/add a property in source; defaults to the Javascript assignment operator.
+ // returns:
+ // dest, as modified
+ // description:
+ // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions
+ // found in Object.prototype, are copied/added to dest. Copying/adding each particular property is
+ // delegated to copyFunc (if any); copyFunc defaults to the Javascript assignment operator if not provided.
+ // Notice that by default, _mixin executes a so-called "shallow copy" and aggregate types are copied/added by reference.
+ var name, s, i, empty = {};
+ for(name in source){
+ // the (!(name in empty) || empty[name] !== s) condition avoids copying properties in "source"
+ // inherited from Object.prototype. For example, if dest has a custom toString() method,
+ // don't overwrite it with the toString() method that source inherited from Object.prototype
+ s = source[name];
+ if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))){
+ dest[name] = copyFunc ? copyFunc(s) : s;
+ }
+ }
+
+ if(has("bug-for-in-skips-shadowed")){
+ if(source){
+ for(i = 0; i < _extraLen; ++i){
+ name = _extraNames[i];
+ s = source[name];
+ if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))){
+ dest[name] = copyFunc ? copyFunc(s) : s;
+ }
+ }
+ }
+ }
+
+ return dest; // Object
+ },
+
+ mixin: function(dest, sources){
+ // summary:
+ // Copies/adds all properties of one or more sources to dest; returns dest.
+ // dest: Object
+ // The object to which to copy/add all properties contained in source. If dest is falsy, then
+ // a new object is manufactured before copying/adding properties begins.
+ // sources: Object...
+ // One of more objects from which to draw all properties to copy into dest. sources are processed
+ // left-to-right and if more than one of these objects contain the same property name, the right-most
+ // value "wins".
+ // returns: Object
+ // dest, as modified
+ // description:
+ // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions
+ // found in Object.prototype, are copied/added from sources to dest. sources are processed left to right.
+ // The Javascript assignment operator is used to copy/add each property; therefore, by default, mixin
+ // executes a so-called "shallow copy" and aggregate types are copied/added by reference.
+ // example:
+ // make a shallow copy of an object
+ // | var copy = lang.mixin({}, source);
+ // example:
+ // many class constructors often take an object which specifies
+ // values to be configured on the object. In this case, it is
+ // often simplest to call `lang.mixin` on the `this` object:
+ // | declare("acme.Base", null, {
+ // | constructor: function(properties){
+ // | // property configuration:
+ // | lang.mixin(this, properties);
+ // |
+ // | console.log(this.quip);
+ // | // ...
+ // | },
+ // | quip: "I wasn't born yesterday, you know - I've seen movies.",
+ // | // ...
+ // | });
+ // |
+ // | // create an instance of the class and configure it
+ // | var b = new acme.Base({quip: "That's what it does!" });
+ // example:
+ // copy in properties from multiple objects
+ // | var flattened = lang.mixin(
+ // | {
+ // | name: "Frylock",
+ // | braces: true
+ // | },
+ // | {
+ // | name: "Carl Brutanananadilewski"
+ // | }
+ // | );
+ // |
+ // | // will print "Carl Brutanananadilewski"
+ // | console.log(flattened.name);
+ // | // will print "true"
+ // | console.log(flattened.braces);
+
+ if(!dest){ dest = {}; }
+ for(var i = 1, l = arguments.length; i < l; i++){
+ lang._mixin(dest, arguments[i]);
+ }
+ return dest; // Object
+ },
+
+ setObject: function(name, value, context){
+ // summary:
+ // Set a property from a dot-separated string, such as "A.B.C"
+ // description:
+ // Useful for longer api chains where you have to test each object in
+ // the chain, or when you have an object reference in string format.
+ // Objects are created as needed along `path`. Returns the passed
+ // value if setting is successful or `undefined` if not.
+ // name: String
+ // Path to a property, in the form "A.B.C".
+ // value: anything
+ // value or object to place at location given by name
+ // context: Object?
+ // Optional. Object to use as root of path. Defaults to
+ // `dojo.global`.
+ // example:
+ // set the value of `foo.bar.baz`, regardless of whether
+ // intermediate objects already exist:
+ // | lang.setObject("foo.bar.baz", value);
+ // example:
+ // without `lang.setObject`, we often see code like this:
+ // | // ensure that intermediate objects are available
+ // | if(!obj["parent"]){ obj.parent = {}; }
+ // | if(!obj.parent["child"]){ obj.parent.child = {}; }
+ // | // now we can safely set the property
+ // | obj.parent.child.prop = "some value";
+ // whereas with `lang.setObject`, we can shorten that to:
+ // | lang.setObject("parent.child.prop", "some value", obj);
+
+ var parts = name.split("."), p = parts.pop(), obj = getProp(parts, true, context);
+ return obj && p ? (obj[p] = value) : undefined; // Object
+ },
+
+ getObject: function(name, create, context){
+ // summary:
+ // Get a property from a dot-separated string, such as "A.B.C"
+ // description:
+ // Useful for longer api chains where you have to test each object in
+ // the chain, or when you have an object reference in string format.
+ // name: String
+ // Path to an property, in the form "A.B.C".
+ // create: Boolean?
+ // Optional. Defaults to `false`. If `true`, Objects will be
+ // created at any point along the 'path' that is undefined.
+ // context: Object?
+ // Optional. Object to use as root of path. Defaults to
+ // 'dojo.global'. Null may be passed.
+ return getProp(name.split("."), create, context); // Object
+ },
+
+ exists: function(name, obj){
+ // summary:
+ // determine if an object supports a given method
+ // description:
+ // useful for longer api chains where you have to test each object in
+ // the chain. Useful for object and method detection.
+ // name: String
+ // Path to an object, in the form "A.B.C".
+ // obj: Object?
+ // Object to use as root of path. Defaults to
+ // 'dojo.global'. Null may be passed.
+ // example:
+ // | // define an object
+ // | var foo = {
+ // | bar: { }
+ // | };
+ // |
+ // | // search the global scope
+ // | lang.exists("foo.bar"); // true
+ // | lang.exists("foo.bar.baz"); // false
+ // |
+ // | // search from a particular scope
+ // | lang.exists("bar", foo); // true
+ // | lang.exists("bar.baz", foo); // false
+ return lang.getObject(name, false, obj) !== undefined; // Boolean
+ },
+
+ // Crockford (ish) functions
+
+ isString: function(it){
+ // summary:
+ // Return true if it is a String
+ // it: anything
+ // Item to test.
+ return (typeof it == "string" || it instanceof String); // Boolean
+ },
+
+ isArray: function(it){
+ // summary:
+ // Return true if it is an Array.
+ // Does not work on Arrays created in other windows.
+ // it: anything
+ // Item to test.
+ return it && (it instanceof Array || typeof it == "array"); // Boolean
+ },
+
+ isFunction: function(it){
+ // summary:
+ // Return true if it is a Function
+ // it: anything
+ // Item to test.
+ return opts.call(it) === "[object Function]";
+ },
+
+ isObject: function(it){
+ // summary:
+ // Returns true if it is a JavaScript object (or an Array, a Function
+ // or null)
+ // it: anything
+ // Item to test.
+ return it !== undefined &&
+ (it === null || typeof it == "object" || lang.isArray(it) || lang.isFunction(it)); // Boolean
+ },
+
+ isArrayLike: function(it){
+ // summary:
+ // similar to isArray() but more permissive
+ // it: anything
+ // Item to test.
+ // returns:
+ // If it walks like a duck and quacks like a duck, return `true`
+ // description:
+ // Doesn't strongly test for "arrayness". Instead, settles for "isn't
+ // a string or number and has a length property". Arguments objects
+ // and DOM collections will return true when passed to
+ // isArrayLike(), but will return false when passed to
+ // isArray().
+ return it && it !== undefined && // Boolean
+ // keep out built-in constructors (Number, String, ...) which have length
+ // properties
+ !lang.isString(it) && !lang.isFunction(it) &&
+ !(it.tagName && it.tagName.toLowerCase() == 'form') &&
+ (lang.isArray(it) || isFinite(it.length));
+ },
+
+ isAlien: function(it){
+ // summary:
+ // Returns true if it is a built-in function or some other kind of
+ // oddball that *should* report as a function but doesn't
+ return it && !lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
+ },
+
+ extend: function(ctor, props){
+ // summary:
+ // Adds all properties and methods of props to constructor's
+ // prototype, making them available to all instances created with
+ // constructor.
+ // ctor: Object
+ // Target constructor to extend.
+ // props: Object
+ // One or more objects to mix into ctor.prototype
+ for(var i=1, l=arguments.length; i 2){
+ return lang._hitchArgs.apply(dojo, arguments); // Function
+ }
+ if(!method){
+ method = scope;
+ scope = null;
+ }
+ if(lang.isString(method)){
+ scope = scope || dojo.global;
+ if(!scope[method]){ throw(['lang.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
+ return function(){ return scope[method].apply(scope, arguments || []); }; // Function
+ }
+ return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function
+ },
+
+ delegate: (function(){
+ // boodman/crockford delegation w/ cornford optimization
+ function TMP(){}
+ return function(obj, props){
+ TMP.prototype = obj;
+ var tmp = new TMP();
+ TMP.prototype = null;
+ if(props){
+ lang._mixin(tmp, props);
+ }
+ return tmp; // Object
+ };
+ })(),
+ /*=====
+ delegate: function(obj, props){
+ // summary:
+ // Returns a new object which "looks" to obj for properties which it
+ // does not have a value for. Optionally takes a bag of properties to
+ // seed the returned object with initially.
+ // description:
+ // This is a small implementation of the Boodman/Crockford delegation
+ // pattern in JavaScript. An intermediate object constructor mediates
+ // the prototype chain for the returned object, using it to delegate
+ // down to obj for property lookup when object-local lookup fails.
+ // This can be thought of similarly to ES4's "wrap", save that it does
+ // not act on types but rather on pure objects.
+ // obj: Object
+ // The object to delegate to for properties not found directly on the
+ // return object or in props.
+ // props: Object...
+ // an object containing properties to assign to the returned object
+ // returns:
+ // an Object of anonymous type
+ // example:
+ // | var foo = { bar: "baz" };
+ // | var thinger = lang.delegate(foo, { thud: "xyzzy"});
+ // | thinger.bar == "baz"; // delegated to foo
+ // | foo.thud == undefined; // by definition
+ // | thinger.thud == "xyzzy"; // mixed in from props
+ // | foo.bar = "thonk";
+ // | thinger.bar == "thonk"; // still delegated to foo's bar
+ },
+ =====*/
+
+ _toArray: has("ie") ?
+ (function(){
+ function slow(obj, offset, startWith){
+ var arr = startWith||[];
+ for(var x = offset || 0; x < obj.length; x++){
+ arr.push(obj[x]);
+ }
+ return arr;
+ }
+ return function(obj){
+ return ((obj.item) ? slow : efficient).apply(this, arguments);
+ };
+ })() : efficient,
+ /*=====
+ _toArray: function(obj, offset, startWith){
+ // summary:
+ // Converts an array-like object (i.e. arguments, DOMCollection) to an
+ // array. Returns a new Array with the elements of obj.
+ // obj: Object
+ // the object to "arrayify". We expect the object to have, at a
+ // minimum, a length property which corresponds to integer-indexed
+ // properties.
+ // offset: Number?
+ // the location in obj to start iterating from. Defaults to 0.
+ // Optional.
+ // startWith: Array?
+ // An array to pack with the properties of obj. If provided,
+ // properties in obj are appended at the end of startWith and
+ // startWith is the returned array.
+ },
+ =====*/
+
+ partial: function(/*Function|String*/ method /*, ...*/){
+ // summary:
+ // similar to hitch() except that the scope object is left to be
+ // whatever the execution context eventually becomes.
+ // description:
+ // Calling lang.partial is the functional equivalent of calling:
+ // | lang.hitch(null, funcName, ...);
+ // method:
+ // The function to "wrap"
+ var arr = [ null ];
+ return lang.hitch.apply(dojo, arr.concat(lang._toArray(arguments))); // Function
+ },
+
+ clone: function(/*anything*/ src){
+ // summary:
+ // Clones objects (including DOM nodes) and all children.
+ // Warning: do not clone cyclic structures.
+ // src:
+ // The object to clone
+ if(!src || typeof src != "object" || lang.isFunction(src)){
+ // null, undefined, any non-object, or function
+ return src; // anything
+ }
+ if(src.nodeType && "cloneNode" in src){
+ // DOM Node
+ return src.cloneNode(true); // Node
+ }
+ if(src instanceof Date){
+ // Date
+ return new Date(src.getTime()); // Date
+ }
+ if(src instanceof RegExp){
+ // RegExp
+ return new RegExp(src); // RegExp
+ }
+ var r, i, l;
+ if(lang.isArray(src)){
+ // array
+ r = [];
+ for(i = 0, l = src.length; i < l; ++i){
+ if(i in src){
+ r.push(lang.clone(src[i]));
+ }
+ }
+ // we don't clone functions for performance reasons
+ // }else if(d.isFunction(src)){
+ // // function
+ // r = function(){ return src.apply(this, arguments); };
+ }else{
+ // generic objects
+ r = src.constructor ? new src.constructor() : {};
+ }
+ return lang._mixin(r, src, lang.clone);
+ },
+
+
+ trim: String.prototype.trim ?
+ function(str){ return str.trim(); } :
+ function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); },
+ /*=====
+ trim: function(str){
+ // summary:
+ // Trims whitespace from both sides of the string
+ // str: String
+ // String to be trimmed
+ // returns: String
+ // Returns the trimmed string
+ // description:
+ // This version of trim() was selected for inclusion into the base due
+ // to its compact size and relatively good performance
+ // (see [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript)
+ // Uses String.prototype.trim instead, if available.
+ // The fastest but longest version of this function is located at
+ // lang.string.trim()
+ },
+ =====*/
+
+ replace: function(tmpl, map, pattern){
+ // summary:
+ // Performs parameterized substitutions on a string. Throws an
+ // exception if any parameter is unmatched.
+ // tmpl: String
+ // String to be used as a template.
+ // map: Object|Function
+ // If an object, it is used as a dictionary to look up substitutions.
+ // If a function, it is called for every substitution with following parameters:
+ // a whole match, a name, an offset, and the whole template
+ // string (see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace
+ // for more details).
+ // pattern: RegEx?
+ // Optional regular expression objects that overrides the default pattern.
+ // Must be global and match one item. The default is: /\{([^\}]+)\}/g,
+ // which matches patterns like that: "{xxx}", where "xxx" is any sequence
+ // of characters, which doesn't include "}".
+ // returns: String
+ // Returns the substituted string.
+ // example:
+ // | // uses a dictionary for substitutions:
+ // | lang.replace("Hello, {name.first} {name.last} AKA {nick}!",
+ // | {
+ // | nick: "Bob",
+ // | name: {
+ // | first: "Robert",
+ // | middle: "X",
+ // | last: "Cringely"
+ // | }
+ // | });
+ // | // returns: Hello, Robert Cringely AKA Bob!
+ // example:
+ // | // uses an array for substitutions:
+ // | lang.replace("Hello, {0} {2}!",
+ // | ["Robert", "X", "Cringely"]);
+ // | // returns: Hello, Robert Cringely!
+ // example:
+ // | // uses a function for substitutions:
+ // | function sum(a){
+ // | var t = 0;
+ // | arrayforEach(a, function(x){ t += x; });
+ // | return t;
+ // | }
+ // | lang.replace(
+ // | "{count} payments averaging {avg} USD per payment.",
+ // | lang.hitch(
+ // | { payments: [11, 16, 12] },
+ // | function(_, key){
+ // | switch(key){
+ // | case "count": return this.payments.length;
+ // | case "min": return Math.min.apply(Math, this.payments);
+ // | case "max": return Math.max.apply(Math, this.payments);
+ // | case "sum": return sum(this.payments);
+ // | case "avg": return sum(this.payments) / this.payments.length;
+ // | }
+ // | }
+ // | )
+ // | );
+ // | // prints: 3 payments averaging 13 USD per payment.
+ // example:
+ // | // uses an alternative PHP-like pattern for substitutions:
+ // | lang.replace("Hello, ${0} ${2}!",
+ // | ["Robert", "X", "Cringely"], /\$\{([^\}]+)\}/g);
+ // | // returns: Hello, Robert Cringely!
+
+ return tmpl.replace(pattern || _pattern, lang.isFunction(map) ?
+ map : function(_, k){ return lang.getObject(k, false, map); });
+ }
+ };
+
+ has("extend-dojo") && lang.mixin(dojo, lang);
+
+ return lang;
+});
+
diff --git a/src/main/resources/static/dojo/_base/loader.js b/src/main/resources/static/dojo/_base/loader.js
new file mode 100644
index 0000000000000000000000000000000000000000..e46a7158da10c630604c5095ab867c040b823bcf
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/loader.js
@@ -0,0 +1,776 @@
+define(["./kernel", "../has", "require", "module", "../json", "./lang", "./array"], function(dojo, has, require, thisModule, json, lang, array) {
+ // module:
+ // dojo/_base/loader
+
+ // This module defines the v1.x synchronous loader API.
+
+ // signal the loader in sync mode...
+ //>>pure-amd
+
+ if (!has("dojo-loader")){
+ console.error("cannot load the Dojo v1.x loader with a foreign loader");
+ return 0;
+ }
+
+ has.add("dojo-fast-sync-require", 1);
+
+
+ var makeErrorToken = function(id){
+ return {src:thisModule.id, id:id};
+ },
+
+ slashName = function(name){
+ return name.replace(/\./g, "/");
+ },
+
+ buildDetectRe = /\/\/>>built/,
+
+ dojoRequireCallbacks = [],
+ dojoRequireModuleStack = [],
+
+ dojoRequirePlugin = function(mid, require, loaded){
+ dojoRequireCallbacks.push(loaded);
+ array.forEach(mid.split(","), function(mid){
+ var module = getModule(mid, require.module);
+ dojoRequireModuleStack.push(module);
+ injectModule(module);
+ });
+ checkDojoRequirePlugin();
+ },
+
+ checkDojoRequirePlugin = (has("dojo-fast-sync-require") ?
+ // This version of checkDojoRequirePlugin makes the observation that all dojoRequireCallbacks can be released
+ // when all *non-dojo/require!, dojo/loadInit!* modules are either executed, not requested, or arrived. This is
+ // the case since there are no more modules the loader is waiting for, therefore, dojo/require! must have
+ // everything it needs on board.
+ //
+ // The potential weakness of this algorithm is that dojo/require will not execute callbacks until *all* dependency
+ // trees are ready. It is possible that some trees may be ready earlier than others, and this extra wait is non-optimal.
+ // Still, for big projects, this seems better than the original algorithm below that proved slow in some cases.
+ // Note, however, the original algorithm had the potential to execute partial trees, but that potential was never enabled.
+ // There are also other optimization available with the original algorithm that have not been explored.
+ function(){
+ var module, mid;
+ for(mid in modules){
+ module = modules[mid];
+ if(module.noReqPluginCheck===undefined){
+ // tag the module as either a loadInit or require plugin or not for future reference
+ module.noReqPluginCheck = /loadInit\!/.test(mid) || /require\!/.test(mid) ? 1 : 0;
+ }
+ if(!module.executed && !module.noReqPluginCheck && module.injected==requested){
+ return;
+ }
+ }
+
+ guardCheckComplete(function(){
+ var oldCallbacks = dojoRequireCallbacks;
+ dojoRequireCallbacks = [];
+ array.forEach(oldCallbacks, function(cb){cb(1);});
+ });
+ } : (function(){
+ // Note: this is the original checkDojoRequirePlugin that is much slower than the algorithm above. However, we know it
+ // works, so we leave it here in case the algorithm above fails in some corner case.
+ //
+ // checkDojoRequirePlugin inspects all of the modules demanded by a dojo/require! dependency
+ // to see if they have arrived. The loader does not release *any* of these modules to be instantiated
+ // until *all* of these modules are on board, thereby preventing the evaluation of a module with dojo.require's
+ // that reference modules that are not available.
+ //
+ // The algorithm works by traversing the dependency graphs (remember, there can be cycles so they are not trees)
+ // of each module in the dojoRequireModuleStack array (which contains the list of modules demanded by dojo/require!).
+ // The moment a single module is discovered that is missing, the algorithm gives up and indicates that not all
+ // modules are on board. dojo/loadInit! and dojo/require! are ignored because there dependencies are inserted
+ // directly in dojoRequireModuleStack. For example, if "your/module" module depends on "dojo/require!my/module", then
+ // *both* "dojo/require!my/module" and "my/module" will be in dojoRequireModuleStack. Obviously, if "my/module"
+ // is on board, then "dojo/require!my/module" is also satisfied, so the algorithm doesn't check for "dojo/require!my/module".
+ //
+ // Note: inserting a dojo/require! dependency in the dojoRequireModuleStack achieves nothing
+ // with the current algorithm; however, having such modules present makes it possible to optimize the algorithm
+ //
+ // Note: prior versions of this algorithm had an optimization that signaled loaded on dojo/require! dependencies
+ // individually (rather than waiting for them all to be resolved). The implementation proved problematic with cycles
+ // and plugins. However, it is possible to reattach that strategy in the future.
+
+ // a set from module-id to {undefined | 1 | 0}, where...
+ // undefined => the module has not been inspected
+ // 0 => the module or at least one of its dependencies has not arrived
+ // 1 => the module is a loadInit! or require! plugin resource, or is currently being traversed (therefore, assume
+ // OK until proven otherwise), or has been completely traversed and all dependencies have arrived
+
+ var touched,
+ traverse = function(m){
+ touched[m.mid] = 1;
+ for(var t, module, deps = m.deps || [], i= 0; i a built module, always AMD
+ // extractResult==0 => no sync API
+ return 0;
+ }
+
+ // manufacture a synthetic module id that can never be a real mdule id (just like require does)
+ id = module.mid + "-*loadInit";
+
+ // construct the dojo/loadInit names vector which causes any relocated names to be defined as lexical variables under their not-relocated name
+ // the dojo/loadInit plugin assumes the first name in names is "dojo"
+
+ for(var p in getModule("dojo", module).result.scopeMap){
+ names.push(p);
+ namesAsStrings.push('"' + p + '"');
+ }
+
+ // rewrite the module as a synthetic dojo/loadInit plugin resource + the module expressed as an AMD module that depends on this synthetic resource
+ // don't have to map dojo/init since that will occur when the dependency is resolved
+ return "// xdomain rewrite of " + module.mid + "\n" +
+ "define('" + id + "',{\n" +
+ "\tnames:" + json.stringify(names) + ",\n" +
+ "\tdef:function(" + names.join(",") + "){" + extractResult[1] + "}" +
+ "});\n\n" +
+ "define(" + json.stringify(names.concat(["dojo/loadInit!"+id])) + ", function(" + names.join(",") + "){\n" + extractResult[0] + "});";
+ },
+
+ loaderVars = require.initSyncLoader(dojoRequirePlugin, checkDojoRequirePlugin, transformToAmd),
+
+ sync =
+ loaderVars.sync,
+
+ requested =
+ loaderVars.requested,
+
+ arrived =
+ loaderVars.arrived,
+
+ nonmodule =
+ loaderVars.nonmodule,
+
+ executing =
+ loaderVars.executing,
+
+ executed =
+ loaderVars.executed,
+
+ syncExecStack =
+ loaderVars.syncExecStack,
+
+ modules =
+ loaderVars.modules,
+
+ execQ =
+ loaderVars.execQ,
+
+ getModule =
+ loaderVars.getModule,
+
+ injectModule =
+ loaderVars.injectModule,
+
+ setArrived =
+ loaderVars.setArrived,
+
+ signal =
+ loaderVars.signal,
+
+ finishExec =
+ loaderVars.finishExec,
+
+ execModule =
+ loaderVars.execModule,
+
+ getLegacyMode =
+ loaderVars.getLegacyMode,
+
+ guardCheckComplete =
+ loaderVars.guardCheckComplete;
+
+ // there is exactly one dojoRequirePlugin among possibly-many dojo/_base/loader's (owing to mapping)
+ dojoRequirePlugin = loaderVars.dojoRequirePlugin;
+
+ dojo.provide = function(mid){
+ var executingModule = syncExecStack[0],
+ module = lang.mixin(getModule(slashName(mid), require.module), {
+ executed:executing,
+ result:lang.getObject(mid, true)
+ });
+ setArrived(module);
+ if(executingModule){
+ (executingModule.provides || (executingModule.provides = [])).push(function(){
+ module.result = lang.getObject(mid);
+ delete module.provides;
+ module.executed!==executed && finishExec(module);
+ });
+ }// else dojo.provide called not consequent to loading; therefore, give up trying to publish module value to loader namespace
+ return module.result;
+ };
+
+ has.add("config-publishRequireResult", 1, 0, 0);
+
+ dojo.require = function(moduleName, omitModuleCheck) {
+ // summary:
+ // loads a Javascript module from the appropriate URI
+ //
+ // moduleName: String
+ // module name to load, using periods for separators,
+ // e.g. "dojo.date.locale". Module paths are de-referenced by dojo's
+ // internal mapping of locations to names and are disambiguated by
+ // longest prefix. See `dojo.registerModulePath()` for details on
+ // registering new modules.
+ //
+ // omitModuleCheck: Boolean?
+ // if `true`, omitModuleCheck skips the step of ensuring that the
+ // loaded file actually defines the symbol it is referenced by.
+ // For example if it called as `dojo.require("a.b.c")` and the
+ // file located at `a/b/c.js` does not define an object `a.b.c`,
+ // and exception will be throws whereas no exception is raised
+ // when called as `dojo.require("a.b.c", true)`
+ //
+ // description:
+ // Modules are loaded via dojo.require by using one of two loaders: the normal loader
+ // and the xdomain loader. The xdomain loader is used when dojo was built with a
+ // custom build that specified loader=xdomain and the module lives on a modulePath
+ // that is a whole URL, with protocol and a domain. The versions of Dojo that are on
+ // the Google and AOL CDNs use the xdomain loader.
+ //
+ // If the module is loaded via the xdomain loader, it is an asynchronous load, since
+ // the module is added via a dynamically created script tag. This
+ // means that dojo.require() can return before the module has loaded. However, this
+ // should only happen in the case where you do dojo.require calls in the top-level
+ // HTML page, or if you purposely avoid the loader checking for dojo.require
+ // dependencies in your module by using a syntax like dojo["require"] to load the module.
+ //
+ // Sometimes it is useful to not have the loader detect the dojo.require calls in the
+ // module so that you can dynamically load the modules as a result of an action on the
+ // page, instead of right at module load time.
+ //
+ // Also, for script blocks in an HTML page, the loader does not pre-process them, so
+ // it does not know to download the modules before the dojo.require calls occur.
+ //
+ // So, in those two cases, when you want on-the-fly module loading or for script blocks
+ // in the HTML page, special care must be taken if the dojo.required code is loaded
+ // asynchronously. To make sure you can execute code that depends on the dojo.required
+ // modules, be sure to add the code that depends on the modules in a dojo.addOnLoad()
+ // callback. dojo.addOnLoad waits for all outstanding modules to finish loading before
+ // executing.
+ //
+ // This type of syntax works with both xdomain and normal loaders, so it is good
+ // practice to always use this idiom for on-the-fly code loading and in HTML script
+ // blocks. If at some point you change loaders and where the code is loaded from,
+ // it will all still work.
+ //
+ // More on how dojo.require
+ // `dojo.require("A.B")` first checks to see if symbol A.B is
+ // defined. If it is, it is simply returned (nothing to do).
+ //
+ // If it is not defined, it will look for `A/B.js` in the script root
+ // directory.
+ //
+ // `dojo.require` throws an exception if it cannot find a file
+ // to load, or if the symbol `A.B` is not defined after loading.
+ //
+ // It returns the object `A.B`, but note the caveats above about on-the-fly loading and
+ // HTML script blocks when the xdomain loader is loading a module.
+ //
+ // `dojo.require()` does nothing about importing symbols into
+ // the current namespace. It is presumed that the caller will
+ // take care of that.
+ //
+ // example:
+ // To use dojo.require in conjunction with dojo.ready:
+ //
+ // | dojo.require("foo");
+ // | dojo.require("bar");
+ // | dojo.addOnLoad(function(){
+ // | //you can now safely do something with foo and bar
+ // | });
+ //
+ // example:
+ // For example, to import all symbols into a local block, you might write:
+ //
+ // | with (dojo.require("A.B")) {
+ // | ...
+ // | }
+ //
+ // And to import just the leaf symbol to a local variable:
+ //
+ // | var B = dojo.require("A.B");
+ // | ...
+ //
+ // returns:
+ // the required namespace object
+ function doRequire(mid, omitModuleCheck){
+ var module = getModule(slashName(mid), require.module);
+ if(syncExecStack.length && syncExecStack[0].finish){
+ // switched to async loading in the middle of evaluating a legacy module; stop
+ // applying dojo.require so the remaining dojo.requires are applied in order
+ syncExecStack[0].finish.push(mid);
+ return undefined;
+ }
+
+ // recall module.executed has values {0, executing, executed}; therefore, truthy indicates executing or executed
+ if(module.executed){
+ return module.result;
+ }
+ omitModuleCheck && (module.result = nonmodule);
+
+ // rcg...why here and in two lines??
+ var currentMode = getLegacyMode();
+
+ // recall, in sync mode to inject is to *eval* the module text
+ // if the module is a legacy module, this is the same as executing
+ // but if the module is an AMD module, this means defining, not executing
+ injectModule(module);
+ // the inject may have changed the mode
+ currentMode = getLegacyMode();
+
+ // in sync mode to dojo.require is to execute
+ if(module.executed!==executed && module.injected===arrived){
+ // the module was already here before injectModule was called probably finishing up a xdomain
+ // load, but maybe a module given to the loader directly rather than having the loader retrieve it
+
+ loaderVars.guardCheckComplete(function(){
+ execModule(module);
+ });
+ }
+ if(module.executed){
+ return module.result;
+ }
+
+ if(currentMode==sync){
+ // the only way to get here is in sync mode and dojo.required a module that
+ // * was loaded async in the injectModule application a few lines up
+ // * was an AMD module that had deps that are being loaded async and therefore couldn't execute
+ if(module.cjs){
+ // the module was an AMD module; unshift, not push, which causes the current traversal to be reattempted from the top
+ execQ.unshift(module);
+ }else{
+ // the module was a legacy module
+ syncExecStack.length && (syncExecStack[0].finish= [mid]);
+ }
+ }else{
+ // the loader wasn't in sync mode on entry; probably async mode; therefore, no expectation of getting
+ // the module value synchronously; make sure it gets executed though
+ execQ.push(module);
+ }
+
+ return undefined;
+ }
+
+ var result = doRequire(moduleName, omitModuleCheck);
+ if(has("config-publishRequireResult") && !lang.exists(moduleName) && result!==undefined){
+ lang.setObject(moduleName, result);
+ }
+ return result;
+ };
+
+ dojo.loadInit = function(f) {
+ f();
+ };
+
+ dojo.registerModulePath = function(/*String*/moduleName, /*String*/prefix){
+ // summary:
+ // Maps a module name to a path
+ // description:
+ // An unregistered module is given the default path of ../[module],
+ // relative to Dojo root. For example, module acme is mapped to
+ // ../acme. If you want to use a different module name, use
+ // dojo.registerModulePath.
+ // example:
+ // If your dojo.js is located at this location in the web root:
+ // | /myapp/js/dojo/dojo/dojo.js
+ // and your modules are located at:
+ // | /myapp/js/foo/bar.js
+ // | /myapp/js/foo/baz.js
+ // | /myapp/js/foo/thud/xyzzy.js
+ // Your application can tell Dojo to locate the "foo" namespace by calling:
+ // | dojo.registerModulePath("foo", "../../foo");
+ // At which point you can then use dojo.require() to load the
+ // modules (assuming they provide() the same things which are
+ // required). The full code might be:
+ // |
+ // |
+
+ var paths = {};
+ paths[moduleName.replace(/\./g, "/")] = prefix;
+ require({paths:paths});
+ };
+
+ dojo.platformRequire = function(/*Object*/modMap){
+ // summary:
+ // require one or more modules based on which host environment
+ // Dojo is currently operating in
+ // description:
+ // This method takes a "map" of arrays which one can use to
+ // optionally load dojo modules. The map is indexed by the
+ // possible dojo.name_ values, with two additional values:
+ // "default" and "common". The items in the "default" array will
+ // be loaded if none of the other items have been choosen based on
+ // dojo.name_, set by your host environment. The items in the
+ // "common" array will *always* be loaded, regardless of which
+ // list is chosen.
+ // example:
+ // | dojo.platformRequire({
+ // | browser: [
+ // | "foo.sample", // simple module
+ // | "foo.test",
+ // | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require)
+ // | ],
+ // | default: [ "foo.sample._base" ],
+ // | common: [ "important.module.common" ]
+ // | });
+
+ var result = (modMap.common || []).concat(modMap[dojo._name] || modMap["default"] || []),
+ temp;
+ while(result.length){
+ if(lang.isArray(temp = result.shift())){
+ dojo.require.apply(dojo, temp);
+ }else{
+ dojo.require(temp);
+ }
+ }
+ };
+
+ dojo.requireIf = dojo.requireAfterIf = function(/*Boolean*/ condition, /*String*/ moduleName, /*Boolean?*/omitModuleCheck){
+ // summary:
+ // If the condition is true then call `dojo.require()` for the specified
+ // resource
+ //
+ // example:
+ // | dojo.requireIf(dojo.isBrowser, "my.special.Module");
+
+ if(condition){
+ dojo.require(moduleName, omitModuleCheck);
+ }
+ };
+
+ dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
+ require(["../i18n"], function(i18n){
+ i18n.getLocalization(moduleName, bundleName, locale);
+ });
+ };
+
+ return {
+ // summary:
+ // This module defines the v1.x synchronous loader API.
+
+ extractLegacyApiApplications:extractLegacyApiApplications,
+ require:dojoRequirePlugin,
+ loadInit:dojoLoadInitPlugin
+ };
+});
diff --git a/src/main/resources/static/dojo/_base/query.js b/src/main/resources/static/dojo/_base/query.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e5516a44ba2d8915ffe38df6640c771117df6c9
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/query.js
@@ -0,0 +1,13 @@
+define(["../query", "./NodeList"], function(query){
+ // module:
+ // dojo/_base/query
+
+ /*=====
+ return {
+ // summary:
+ // Deprecated. Use dojo/query instead.
+ };
+ =====*/
+
+ return query;
+});
diff --git a/src/main/resources/static/dojo/_base/sniff.js b/src/main/resources/static/dojo/_base/sniff.js
new file mode 100644
index 0000000000000000000000000000000000000000..979c1e7b2c21336f74a0a10b74228b90806ebea8
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/sniff.js
@@ -0,0 +1,93 @@
+define(["./kernel", "./lang", "../sniff"], function(dojo, lang, has){
+ // module:
+ // dojo/_base/sniff
+
+ /*=====
+ return {
+ // summary:
+ // Deprecated. New code should use dojo/sniff.
+ // This module populates the dojo browser version sniffing properties like dojo.isIE.
+ };
+ =====*/
+
+ if(!has("host-browser")){
+ return has;
+ }
+
+ // no idea what this is for, or if it's used
+ dojo._name = "browser";
+
+ lang.mixin(dojo, {
+ // isBrowser: Boolean
+ // True if the client is a web-browser
+ isBrowser: true,
+
+ // isFF: Number|undefined
+ // Version as a Number if client is FireFox. undefined otherwise. Corresponds to
+ // major detected FireFox version (1.5, 2, 3, etc.)
+ isFF: has("ff"),
+
+ // isIE: Number|undefined
+ // Version as a Number if client is MSIE(PC). undefined otherwise. Corresponds to
+ // major detected IE version (6, 7, 8, etc.)
+ isIE: has("ie"),
+
+ // isKhtml: Number|undefined
+ // Version as a Number if client is a KHTML browser. undefined otherwise. Corresponds to major
+ // detected version.
+ isKhtml: has("khtml"),
+
+ // isWebKit: Number|undefined
+ // Version as a Number if client is a WebKit-derived browser (Konqueror,
+ // Safari, Chrome, etc.). undefined otherwise.
+ isWebKit: has("webkit"),
+
+ // isMozilla: Number|undefined
+ // Version as a Number if client is a Mozilla-based browser (Firefox,
+ // SeaMonkey). undefined otherwise. Corresponds to major detected version.
+ isMozilla: has("mozilla"),
+ // isMoz: Number|undefined
+ // Version as a Number if client is a Mozilla-based browser (Firefox,
+ // SeaMonkey). undefined otherwise. Corresponds to major detected version.
+ isMoz: has("mozilla"),
+
+ // isOpera: Number|undefined
+ // Version as a Number if client is Opera. undefined otherwise. Corresponds to
+ // major detected version.
+ isOpera: has("opera"),
+
+ // isSafari: Number|undefined
+ // Version as a Number if client is Safari or iPhone. undefined otherwise.
+ isSafari: has("safari"),
+
+ // isChrome: Number|undefined
+ // Version as a Number if client is Chrome browser. undefined otherwise.
+ isChrome: has("chrome"),
+
+ // isMac: Boolean
+ // True if the client runs on Mac
+ isMac: has("mac"),
+
+ // isIos: Number|undefined
+ // Version as a Number if client is iPhone, iPod, or iPad. undefined otherwise.
+ isIos: has("ios"),
+
+ // isAndroid: Number|undefined
+ // Version as a Number if client is android browser. undefined otherwise.
+ isAndroid: has("android"),
+
+ // isWii: Boolean
+ // True if client is Wii
+ isWii: has("wii"),
+
+ // isQuirks: Boolean
+ // Page is in quirks mode.
+ isQuirks: has("quirks"),
+
+ // isAir: Boolean
+ // True if client is Adobe Air
+ isAir: has("air")
+ });
+
+ return has;
+});
diff --git a/src/main/resources/static/dojo/_base/unload.js b/src/main/resources/static/dojo/_base/unload.js
new file mode 100644
index 0000000000000000000000000000000000000000..60a9a7e262cfdbc0bebe8f02815ccb3a26b8041d
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/unload.js
@@ -0,0 +1,82 @@
+define(["./kernel", "./lang", "../on"], function(dojo, lang, on){
+
+// module:
+// dojo/unload
+
+var win = window;
+
+var unload = {
+ // summary:
+ // This module contains the document and window unload detection API.
+
+ addOnWindowUnload: function(/*Object|Function?*/ obj, /*String|Function?*/ functionName){
+ // summary:
+ // registers a function to be triggered when window.onunload
+ // fires.
+ // description:
+ // The first time that addOnWindowUnload is called Dojo
+ // will register a page listener to trigger your unload
+ // handler with. Note that registering these handlers may
+ // destroy "fastback" page caching in browsers that support
+ // it. Be careful trying to modify the DOM or access
+ // JavaScript properties during this phase of page unloading:
+ // they may not always be available. Consider
+ // addOnUnload() if you need to modify the DOM or do
+ // heavy JavaScript work since it fires at the equivalent of
+ // the page's "onbeforeunload" event.
+ // example:
+ // | unload.addOnWindowUnload(functionPointer)
+ // | unload.addOnWindowUnload(object, "functionName");
+ // | unload.addOnWindowUnload(object, function(){ /* ... */});
+
+ if (!dojo.windowUnloaded){
+ on(win, "unload", (dojo.windowUnloaded = function(){
+ // summary:
+ // signal fired by impending window destruction. You may use
+ // dojo.addOnWindowUnload() to register a listener for this
+ // event. NOTE: if you wish to dojo.connect() to this method
+ // to perform page/application cleanup, be aware that this
+ // event WILL NOT fire if no handler has been registered with
+ // addOnWindowUnload(). This behavior started in Dojo 1.3.
+ // Previous versions always triggered windowUnloaded(). See
+ // addOnWindowUnload for more info.
+ }));
+ }
+ on(win, "unload", lang.hitch(obj, functionName));
+ },
+
+ addOnUnload: function(/*Object?|Function?*/ obj, /*String|Function?*/ functionName){
+ // summary:
+ // registers a function to be triggered when the page unloads.
+ // description:
+ // The first time that addOnUnload is called Dojo will
+ // register a page listener to trigger your unload handler
+ // with.
+ //
+ // In a browser environment, the functions will be triggered
+ // during the window.onbeforeunload event. Be careful of doing
+ // too much work in an unload handler. onbeforeunload can be
+ // triggered if a link to download a file is clicked, or if
+ // the link is a javascript: link. In these cases, the
+ // onbeforeunload event fires, but the document is not
+ // actually destroyed. So be careful about doing destructive
+ // operations in a dojo.addOnUnload callback.
+ //
+ // Further note that calling dojo.addOnUnload will prevent
+ // browsers from using a "fast back" cache to make page
+ // loading via back button instantaneous.
+ // example:
+ // | dojo.addOnUnload(functionPointer)
+ // | dojo.addOnUnload(object, "functionName")
+ // | dojo.addOnUnload(object, function(){ /* ... */});
+
+ on(win, "beforeunload", lang.hitch(obj, functionName));
+ }
+};
+
+dojo.addOnWindowUnload = unload.addOnWindowUnload;
+dojo.addOnUnload = unload.addOnUnload;
+
+return unload;
+
+});
diff --git a/src/main/resources/static/dojo/_base/url.js b/src/main/resources/static/dojo/_base/url.js
new file mode 100644
index 0000000000000000000000000000000000000000..b358b6a1466bd0f75ac42d2e041390d288282a86
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/url.js
@@ -0,0 +1,109 @@
+define(["./kernel"], function(dojo){
+ // module:
+ // dojo/url
+
+ var
+ ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
+ ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
+ _Url = function(){
+ var n = null,
+ _a = arguments,
+ uri = [_a[0]];
+ // resolve uri components relative to each other
+ for(var i = 1; i<_a.length; i++){
+ if(!_a[i]){ continue; }
+
+ // Safari doesn't support this.constructor so we have to be explicit
+ // FIXME: Tracked (and fixed) in Webkit bug 3537.
+ // http://bugs.webkit.org/show_bug.cgi?id=3537
+ var relobj = new _Url(_a[i]+""),
+ uriobj = new _Url(uri[0]+"");
+
+ if(
+ relobj.path == "" &&
+ !relobj.scheme &&
+ !relobj.authority &&
+ !relobj.query
+ ){
+ if(relobj.fragment != n){
+ uriobj.fragment = relobj.fragment;
+ }
+ relobj = uriobj;
+ }else if(!relobj.scheme){
+ relobj.scheme = uriobj.scheme;
+
+ if(!relobj.authority){
+ relobj.authority = uriobj.authority;
+
+ if(relobj.path.charAt(0) != "/"){
+ var path = uriobj.path.substring(0,
+ uriobj.path.lastIndexOf("/") + 1) + relobj.path;
+
+ var segs = path.split("/");
+ for(var j = 0; j < segs.length; j++){
+ if(segs[j] == "."){
+ // flatten "./" references
+ if(j == segs.length - 1){
+ segs[j] = "";
+ }else{
+ segs.splice(j, 1);
+ j--;
+ }
+ }else if(j > 0 && !(j == 1 && segs[0] == "") &&
+ segs[j] == ".." && segs[j-1] != ".."){
+ // flatten "../" references
+ if(j == (segs.length - 1)){
+ segs.splice(j, 1);
+ segs[j - 1] = "";
+ }else{
+ segs.splice(j - 1, 2);
+ j -= 2;
+ }
+ }
+ }
+ relobj.path = segs.join("/");
+ }
+ }
+ }
+
+ uri = [];
+ if(relobj.scheme){
+ uri.push(relobj.scheme, ":");
+ }
+ if(relobj.authority){
+ uri.push("//", relobj.authority);
+ }
+ uri.push(relobj.path);
+ if(relobj.query){
+ uri.push("?", relobj.query);
+ }
+ if(relobj.fragment){
+ uri.push("#", relobj.fragment);
+ }
+ }
+
+ this.uri = uri.join("");
+
+ // break the uri into its main components
+ var r = this.uri.match(ore);
+
+ this.scheme = r[2] || (r[1] ? "" : n);
+ this.authority = r[4] || (r[3] ? "" : n);
+ this.path = r[5]; // can never be undefined
+ this.query = r[7] || (r[6] ? "" : n);
+ this.fragment = r[9] || (r[8] ? "" : n);
+
+ if(this.authority != n){
+ // server based naming authority
+ r = this.authority.match(ire);
+
+ this.user = r[3] || n;
+ this.password = r[4] || n;
+ this.host = r[6] || r[7]; // ipv6 || ipv4
+ this.port = r[9] || n;
+ }
+ };
+ _Url.prototype.toString = function(){ return this.uri; };
+
+ return dojo._Url = _Url;
+});
diff --git a/src/main/resources/static/dojo/_base/window.js b/src/main/resources/static/dojo/_base/window.js
new file mode 100644
index 0000000000000000000000000000000000000000..df479b111dd57553b528278aa4030b7ce4838d38
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/window.js
@@ -0,0 +1,134 @@
+define(["./kernel", "./lang", "../sniff"], function(dojo, lang, has){
+// module:
+// dojo/_base/window
+
+var ret = {
+ // summary:
+ // API to save/set/restore the global/document scope.
+
+ global: dojo.global,
+ /*=====
+ global: {
+ // summary:
+ // Alias for the current window. 'global' can be modified
+ // for temporary context shifting. See also withGlobal().
+ // description:
+ // Use this rather than referring to 'window' to ensure your code runs
+ // correctly in managed contexts.
+ },
+ =====*/
+
+ doc: this["document"] || null,
+ /*=====
+ doc: {
+ // summary:
+ // Alias for the current document. 'doc' can be modified
+ // for temporary context shifting. See also withDoc().
+ // description:
+ // Use this rather than referring to 'window.document' to ensure your code runs
+ // correctly in managed contexts.
+ // example:
+ // | n.appendChild(dojo.doc.createElement('div'));
+ },
+ =====*/
+
+ body: function(/*Document?*/ doc){
+ // summary:
+ // Return the body element of the specified document or of dojo/_base/window::doc.
+ // example:
+ // | win.body().appendChild(dojo.doc.createElement('div'));
+
+ // Note: document.body is not defined for a strict xhtml document
+ // Would like to memoize this, but dojo.doc can change vi dojo.withDoc().
+ doc = doc || dojo.doc;
+ return doc.body || doc.getElementsByTagName("body")[0]; // Node
+ },
+
+ setContext: function(/*Object*/ globalObject, /*DocumentElement*/ globalDocument){
+ // summary:
+ // changes the behavior of many core Dojo functions that deal with
+ // namespace and DOM lookup, changing them to work in a new global
+ // context (e.g., an iframe). The varibles dojo.global and dojo.doc
+ // are modified as a result of calling this function and the result of
+ // `dojo.body()` likewise differs.
+ dojo.global = ret.global = globalObject;
+ dojo.doc = ret.doc = globalDocument;
+ },
+
+ withGlobal: function( /*Object*/ globalObject,
+ /*Function*/ callback,
+ /*Object?*/ thisObject,
+ /*Array?*/ cbArguments){
+ // summary:
+ // Invoke callback with globalObject as dojo.global and
+ // globalObject.document as dojo.doc.
+ // description:
+ // Invoke callback with globalObject as dojo.global and
+ // globalObject.document as dojo.doc. If provided, globalObject
+ // will be executed in the context of object thisObject
+ // When callback() returns or throws an error, the dojo.global
+ // and dojo.doc will be restored to its previous state.
+
+ var oldGlob = dojo.global;
+ try{
+ dojo.global = ret.global = globalObject;
+ return ret.withDoc.call(null, globalObject.document, callback, thisObject, cbArguments);
+ }finally{
+ dojo.global = ret.global = oldGlob;
+ }
+ },
+
+ withDoc: function( /*DocumentElement*/ documentObject,
+ /*Function*/ callback,
+ /*Object?*/ thisObject,
+ /*Array?*/ cbArguments){
+ // summary:
+ // Invoke callback with documentObject as dojo/_base/window::doc.
+ // description:
+ // Invoke callback with documentObject as dojo/_base/window::doc. If provided,
+ // callback will be executed in the context of object thisObject
+ // When callback() returns or throws an error, the dojo/_base/window::doc will
+ // be restored to its previous state.
+
+ var oldDoc = ret.doc,
+ oldQ = has("quirks"),
+ oldIE = has("ie"), isIE, mode, pwin;
+
+ try{
+ dojo.doc = ret.doc = documentObject;
+ // update dojo.isQuirks and the value of the has feature "quirks".
+ // remove setting dojo.isQuirks and dojo.isIE for 2.0
+ dojo.isQuirks = has.add("quirks", dojo.doc.compatMode == "BackCompat", true, true); // no need to check for QuirksMode which was Opera 7 only
+
+ if(has("ie")){
+ if((pwin = documentObject.parentWindow) && pwin.navigator){
+ // re-run IE detection logic and update dojo.isIE / has("ie")
+ // (the only time parentWindow/navigator wouldn't exist is if we were not
+ // passed an actual legitimate document object)
+ isIE = parseFloat(pwin.navigator.appVersion.split("MSIE ")[1]) || undefined;
+ mode = documentObject.documentMode;
+ if(mode && mode != 5 && Math.floor(isIE) != mode){
+ isIE = mode;
+ }
+ dojo.isIE = has.add("ie", isIE, true, true);
+ }
+ }
+
+ if(thisObject && typeof callback == "string"){
+ callback = thisObject[callback];
+ }
+
+ return callback.apply(thisObject, cbArguments || []);
+ }finally{
+ dojo.doc = ret.doc = oldDoc;
+ dojo.isQuirks = has.add("quirks", oldQ, true, true);
+ dojo.isIE = has.add("ie", oldIE, true, true);
+ }
+ }
+};
+
+has("extend-dojo") && lang.mixin(dojo, ret);
+
+return ret;
+
+});
diff --git a/src/main/resources/static/dojo/_base/xhr.js b/src/main/resources/static/dojo/_base/xhr.js
new file mode 100644
index 0000000000000000000000000000000000000000..0d0db8ef1becdbcac5dec71cf397e400fddf9369
--- /dev/null
+++ b/src/main/resources/static/dojo/_base/xhr.js
@@ -0,0 +1,718 @@
+define([
+ "./kernel",
+ "./sniff",
+ "require",
+ "../io-query",
+ /*===== "./declare", =====*/
+ "../dom",
+ "../dom-form",
+ "./Deferred",
+ "./config",
+ "./json",
+ "./lang",
+ "./array",
+ "../on",
+ "../aspect",
+ "../request/watch",
+ "../request/xhr",
+ "../request/util"
+], function(dojo, has, require, ioq, /*===== declare, =====*/ dom, domForm, Deferred, config, json, lang, array, on, aspect, watch, _xhr, util){
+ // module:
+ // dojo/_base/xhr
+
+ /*=====
+ dojo._xhrObj = function(){
+ // summary:
+ // does the work of portably generating a new XMLHTTPRequest object.
+ };
+ =====*/
+ dojo._xhrObj = _xhr._create;
+
+ var cfg = dojo.config;
+
+ // mix in io-query and dom-form
+ dojo.objectToQuery = ioq.objectToQuery;
+ dojo.queryToObject = ioq.queryToObject;
+ dojo.fieldToObject = domForm.fieldToObject;
+ dojo.formToObject = domForm.toObject;
+ dojo.formToQuery = domForm.toQuery;
+ dojo.formToJson = domForm.toJson;
+
+ // need to block async callbacks from snatching this thread as the result
+ // of an async callback might call another sync XHR, this hangs khtml forever
+ // must checked by watchInFlight()
+
+ dojo._blockAsync = false;
+
+ // MOW: remove dojo._contentHandlers alias in 2.0
+ var handlers = dojo._contentHandlers = dojo.contentHandlers = {
+ // summary:
+ // A map of available XHR transport handle types. Name matches the
+ // `handleAs` attribute passed to XHR calls.
+ // description:
+ // A map of available XHR transport handle types. Name matches the
+ // `handleAs` attribute passed to XHR calls. Each contentHandler is
+ // called, passing the xhr object for manipulation. The return value
+ // from the contentHandler will be passed to the `load` or `handle`
+ // functions defined in the original xhr call.
+ // example:
+ // Creating a custom content-handler:
+ // | xhr.contentHandlers.makeCaps = function(xhr){
+ // | return xhr.responseText.toUpperCase();
+ // | }
+ // | // and later:
+ // | dojo.xhrGet({
+ // | url:"foo.txt",
+ // | handleAs:"makeCaps",
+ // | load: function(data){ /* data is a toUpper version of foo.txt */ }
+ // | });
+
+ "text": function(xhr){
+ // summary:
+ // A contentHandler which simply returns the plaintext response data
+ return xhr.responseText;
+ },
+ "json": function(xhr){
+ // summary:
+ // A contentHandler which returns a JavaScript object created from the response data
+ return json.fromJson(xhr.responseText || null);
+ },
+ "json-comment-filtered": function(xhr){
+ // summary:
+ // A contentHandler which expects comment-filtered JSON.
+ // description:
+ // A contentHandler which expects comment-filtered JSON.
+ // the json-comment-filtered option was implemented to prevent
+ // "JavaScript Hijacking", but it is less secure than standard JSON. Use
+ // standard JSON instead. JSON prefixing can be used to subvert hijacking.
+ //
+ // Will throw a notice suggesting to use application/json mimetype, as
+ // json-commenting can introduce security issues. To decrease the chances of hijacking,
+ // use the standard `json` contentHandler, and prefix your "JSON" with: {}&&
+ //
+ // use djConfig.useCommentedJson = true to turn off the notice
+ if(!config.useCommentedJson){
+ console.warn("Consider using the standard mimetype:application/json."
+ + " json-commenting can introduce security issues. To"
+ + " decrease the chances of hijacking, use the standard the 'json' handler and"
+ + " prefix your json with: {}&&\n"
+ + "Use djConfig.useCommentedJson=true to turn off this message.");
+ }
+
+ var value = xhr.responseText;
+ var cStartIdx = value.indexOf("\/*");
+ var cEndIdx = value.lastIndexOf("*\/");
+ if(cStartIdx == -1 || cEndIdx == -1){
+ throw new Error("JSON was not comment filtered");
+ }
+ return json.fromJson(value.substring(cStartIdx+2, cEndIdx));
+ },
+ "javascript": function(xhr){
+ // summary:
+ // A contentHandler which evaluates the response data, expecting it to be valid JavaScript
+
+ // FIXME: try Moz and IE specific eval variants?
+ return dojo.eval(xhr.responseText);
+ },
+ "xml": function(xhr){
+ // summary:
+ // A contentHandler returning an XML Document parsed from the response data
+ var result = xhr.responseXML;
+
+ if(result && has("dom-qsa2.1") && !result.querySelectorAll && has("dom-parser")){
+ // http://bugs.dojotoolkit.org/ticket/15631
+ // IE9 supports a CSS3 querySelectorAll implementation, but the DOM implementation
+ // returned by IE9 xhr.responseXML does not. Manually create the XML DOM to gain
+ // the fuller-featured implementation and avoid bugs caused by the inconsistency
+ result = new DOMParser().parseFromString(xhr.responseText, "application/xml");
+ }
+
+ if(has("ie")){
+ if((!result || !result.documentElement)){
+ //WARNING: this branch used by the xml handling in dojo.io.iframe,
+ //so be sure to test dojo.io.iframe if making changes below.
+ var ms = function(n){ return "MSXML" + n + ".DOMDocument"; };
+ var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)];
+ array.some(dp, function(p){
+ try{
+ var dom = new ActiveXObject(p);
+ dom.async = false;
+ dom.loadXML(xhr.responseText);
+ result = dom;
+ }catch(e){ return false; }
+ return true;
+ });
+ }
+ }
+ return result; // DOMDocument
+ },
+ "json-comment-optional": function(xhr){
+ // summary:
+ // A contentHandler which checks the presence of comment-filtered JSON and
+ // alternates between the `json` and `json-comment-filtered` contentHandlers.
+ if(xhr.responseText && /^[^{\[]*\/\*/.test(xhr.responseText)){
+ return handlers["json-comment-filtered"](xhr);
+ }else{
+ return handlers["json"](xhr);
+ }
+ }
+ };
+
+ /*=====
+
+ // kwargs function parameter definitions. Assigning to dojo namespace rather than making them local variables
+ // because they are used by dojo/io modules too
+
+ dojo.__IoArgs = declare(null, {
+ // url: String
+ // URL to server endpoint.
+ // content: Object?
+ // Contains properties with string values. These
+ // properties will be serialized as name1=value2 and
+ // passed in the request.
+ // timeout: Integer?
+ // Milliseconds to wait for the response. If this time
+ // passes, the then error callbacks are called.
+ // form: DOMNode?
+ // DOM node for a form. Used to extract the form values
+ // and send to the server.
+ // preventCache: Boolean?
+ // Default is false. If true, then a
+ // "dojo.preventCache" parameter is sent in the request
+ // with a value that changes with each request
+ // (timestamp). Useful only with GET-type requests.
+ // handleAs: String?
+ // Acceptable values depend on the type of IO
+ // transport (see specific IO calls for more information).
+ // rawBody: String?
+ // Sets the raw body for an HTTP request. If this is used, then the content
+ // property is ignored. This is mostly useful for HTTP methods that have
+ // a body to their requests, like PUT or POST. This property can be used instead
+ // of postData and putData for dojo/_base/xhr.rawXhrPost and dojo/_base/xhr.rawXhrPut respectively.
+ // ioPublish: Boolean?
+ // Set this explicitly to false to prevent publishing of topics related to
+ // IO operations. Otherwise, if djConfig.ioPublish is set to true, topics
+ // will be published via dojo/topic.publish() for different phases of an IO operation.
+ // See dojo/main.__IoPublish for a list of topics that are published.
+
+ load: function(response, ioArgs){
+ // summary:
+ // This function will be
+ // called on a successful HTTP response code.
+ // ioArgs: dojo/main.__IoCallbackArgs
+ // Provides additional information about the request.
+ // response: Object
+ // The response in the format as defined with handleAs.
+ },
+
+ error: function(response, ioArgs){
+ // summary:
+ // This function will
+ // be called when the request fails due to a network or server error, the url
+ // is invalid, etc. It will also be called if the load or handle callback throws an
+ // exception, unless djConfig.debugAtAllCosts is true. This allows deployed applications
+ // to continue to run even when a logic error happens in the callback, while making
+ // it easier to troubleshoot while in debug mode.
+ // ioArgs: dojo/main.__IoCallbackArgs
+ // Provides additional information about the request.
+ // response: Object
+ // The response in the format as defined with handleAs.
+ },
+
+ handle: function(loadOrError, response, ioArgs){
+ // summary:
+ // This function will
+ // be called at the end of every request, whether or not an error occurs.
+ // loadOrError: String
+ // Provides a string that tells you whether this function
+ // was called because of success (load) or failure (error).
+ // response: Object
+ // The response in the format as defined with handleAs.
+ // ioArgs: dojo/main.__IoCallbackArgs
+ // Provides additional information about the request.
+ }
+ });
+
+ dojo.__IoCallbackArgs = declare(null, {
+ // args: Object
+ // the original object argument to the IO call.
+ // xhr: XMLHttpRequest
+ // For XMLHttpRequest calls only, the
+ // XMLHttpRequest object that was used for the
+ // request.
+ // url: String
+ // The final URL used for the call. Many times it
+ // will be different than the original args.url
+ // value.
+ // query: String
+ // For non-GET requests, the
+ // name1=value1&name2=value2 parameters sent up in
+ // the request.
+ // handleAs: String
+ // The final indicator on how the response will be
+ // handled.
+ // id: String
+ // For dojo/io/script calls only, the internal
+ // script ID used for the request.
+ // canDelete: Boolean
+ // For dojo/io/script calls only, indicates
+ // whether the script tag that represents the
+ // request can be deleted after callbacks have
+ // been called. Used internally to know when
+ // cleanup can happen on JSONP-type requests.
+ // json: Object
+ // For dojo/io/script calls only: holds the JSON
+ // response for JSONP-type requests. Used
+ // internally to hold on to the JSON responses.
+ // You should not need to access it directly --
+ // the same object should be passed to the success
+ // callbacks directly.
+ });
+
+ dojo.__IoPublish = declare(null, {
+ // summary:
+ // This is a list of IO topics that can be published
+ // if djConfig.ioPublish is set to true. IO topics can be
+ // published for any Input/Output, network operation. So,
+ // dojo.xhr, dojo.io.script and dojo.io.iframe can all
+ // trigger these topics to be published.
+ // start: String
+ // "/dojo/io/start" is sent when there are no outstanding IO
+ // requests, and a new IO request is started. No arguments
+ // are passed with this topic.
+ // send: String
+ // "/dojo/io/send" is sent whenever a new IO request is started.
+ // It passes the dojo.Deferred for the request with the topic.
+ // load: String
+ // "/dojo/io/load" is sent whenever an IO request has loaded
+ // successfully. It passes the response and the dojo.Deferred
+ // for the request with the topic.
+ // error: String
+ // "/dojo/io/error" is sent whenever an IO request has errored.
+ // It passes the error and the dojo.Deferred
+ // for the request with the topic.
+ // done: String
+ // "/dojo/io/done" is sent whenever an IO request has completed,
+ // either by loading or by erroring. It passes the error and
+ // the dojo.Deferred for the request with the topic.
+ // stop: String
+ // "/dojo/io/stop" is sent when all outstanding IO requests have
+ // finished. No arguments are passed with this topic.
+ });
+ =====*/
+
+
+ dojo._ioSetArgs = function(/*dojo/main.__IoArgs*/args,
+ /*Function*/canceller,
+ /*Function*/okHandler,
+ /*Function*/errHandler){
+ // summary:
+ // sets up the Deferred and ioArgs property on the Deferred so it
+ // can be used in an io call.
+ // args:
+ // The args object passed into the public io call. Recognized properties on
+ // the args object are:
+ // canceller:
+ // The canceller function used for the Deferred object. The function
+ // will receive one argument, the Deferred object that is related to the
+ // canceller.
+ // okHandler:
+ // The first OK callback to be registered with Deferred. It has the opportunity
+ // to transform the OK response. It will receive one argument -- the Deferred
+ // object returned from this function.
+ // errHandler:
+ // The first error callback to be registered with Deferred. It has the opportunity
+ // to do cleanup on an error. It will receive two arguments: error (the
+ // Error object) and dfd, the Deferred object returned from this function.
+
+ var ioArgs = {args: args, url: args.url};
+
+ //Get values from form if requested.
+ var formObject = null;
+ if(args.form){
+ var form = dom.byId(args.form);
+ //IE requires going through getAttributeNode instead of just getAttribute in some form cases,
+ //so use it for all. See #2844
+ var actnNode = form.getAttributeNode("action");
+ ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null);
+ formObject = domForm.toObject(form);
+ }
+
+ // set up the query params
+ var miArgs = [{}];
+
+ if(formObject){
+ // potentially over-ride url-provided params w/ form values
+ miArgs.push(formObject);
+ }
+ if(args.content){
+ // stuff in content over-rides what's set by form
+ miArgs.push(args.content);
+ }
+ if(args.preventCache){
+ miArgs.push({"dojo.preventCache": new Date().valueOf()});
+ }
+ ioArgs.query = ioq.objectToQuery(lang.mixin.apply(null, miArgs));
+
+ // .. and the real work of getting the deferred in order, etc.
+ ioArgs.handleAs = args.handleAs || "text";
+ var d = new Deferred(function(dfd){
+ dfd.canceled = true;
+ canceller && canceller(dfd);
+
+ var err = dfd.ioArgs.error;
+ if(!err){
+ err = new Error("request cancelled");
+ err.dojoType="cancel";
+ dfd.ioArgs.error = err;
+ }
+ return err;
+ });
+ d.addCallback(okHandler);
+
+ //Support specifying load, error and handle callback functions from the args.
+ //For those callbacks, the "this" object will be the args object.
+ //The callbacks will get the deferred result value as the
+ //first argument and the ioArgs object as the second argument.
+ var ld = args.load;
+ if(ld && lang.isFunction(ld)){
+ d.addCallback(function(value){
+ return ld.call(args, value, ioArgs);
+ });
+ }
+ var err = args.error;
+ if(err && lang.isFunction(err)){
+ d.addErrback(function(value){
+ return err.call(args, value, ioArgs);
+ });
+ }
+ var handle = args.handle;
+ if(handle && lang.isFunction(handle)){
+ d.addBoth(function(value){
+ return handle.call(args, value, ioArgs);
+ });
+ }
+
+ // Attach error handler last (not including topic publishing)
+ // to catch any errors that may have been generated from load
+ // or handle functions.
+ d.addErrback(function(error){
+ return errHandler(error, d);
+ });
+
+ //Plug in topic publishing, if dojo.publish is loaded.
+ if(cfg.ioPublish && dojo.publish && ioArgs.args.ioPublish !== false){
+ d.addCallbacks(
+ function(res){
+ dojo.publish("/dojo/io/load", [d, res]);
+ return res;
+ },
+ function(res){
+ dojo.publish("/dojo/io/error", [d, res]);
+ return res;
+ }
+ );
+ d.addBoth(function(res){
+ dojo.publish("/dojo/io/done", [d, res]);
+ return res;
+ });
+ }
+
+ d.ioArgs = ioArgs;
+
+ // FIXME: need to wire up the xhr object's abort method to something
+ // analogous in the Deferred
+ return d;
+ };
+
+ var _deferredOk = function(/*Deferred*/dfd){
+ // summary:
+ // okHandler function for dojo._ioSetArgs call.
+
+ var ret = handlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
+ return ret === undefined ? null : ret;
+ };
+ var _deferError = function(/*Error*/error, /*Deferred*/dfd){
+ // summary:
+ // errHandler function for dojo._ioSetArgs call.
+
+ if(!dfd.ioArgs.args.failOk){
+ console.error(error);
+ }
+ return error;
+ };
+
+ //Use a separate count for knowing if we are starting/stopping io calls.
+ var _checkPubCount = function(dfd){
+ if(_pubCount <= 0){
+ _pubCount = 0;
+ if(cfg.ioPublish && dojo.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){
+ dojo.publish("/dojo/io/stop");
+ }
+ }
+ };
+
+ var _pubCount = 0;
+ aspect.after(watch, "_onAction", function(){
+ _pubCount -= 1;
+ });
+ aspect.after(watch, "_onInFlight", _checkPubCount);
+
+ dojo._ioCancelAll = watch.cancelAll;
+ /*=====
+ dojo._ioCancelAll = function(){
+ // summary:
+ // Cancels all pending IO requests, regardless of IO type
+ // (xhr, script, iframe).
+ };
+ =====*/
+
+ dojo._ioNotifyStart = function(/*Deferred*/dfd){
+ // summary:
+ // If dojo.publish is available, publish topics
+ // about the start of a request queue and/or the
+ // the beginning of request.
+ //
+ // Used by IO transports. An IO transport should
+ // call this method before making the network connection.
+ if(cfg.ioPublish && dojo.publish && dfd.ioArgs.args.ioPublish !== false){
+ if(!_pubCount){
+ dojo.publish("/dojo/io/start");
+ }
+ _pubCount += 1;
+ dojo.publish("/dojo/io/send", [dfd]);
+ }
+ };
+
+ dojo._ioWatch = function(dfd, validCheck, ioCheck, resHandle){
+ // summary:
+ // Watches the io request represented by dfd to see if it completes.
+ // dfd: Deferred
+ // The Deferred object to watch.
+ // validCheck: Function
+ // Function used to check if the IO request is still valid. Gets the dfd
+ // object as its only argument.
+ // ioCheck: Function
+ // Function used to check if basic IO call worked. Gets the dfd
+ // object as its only argument.
+ // resHandle: Function
+ // Function used to process response. Gets the dfd
+ // object as its only argument.
+
+ var args = dfd.ioArgs.options = dfd.ioArgs.args;
+ lang.mixin(dfd, {
+ response: dfd.ioArgs,
+ isValid: function(response){
+ return validCheck(dfd);
+ },
+ isReady: function(response){
+ return ioCheck(dfd);
+ },
+ handleResponse: function(response){
+ return resHandle(dfd);
+ }
+ });
+ watch(dfd);
+
+ _checkPubCount(dfd);
+ };
+
+ var _defaultContentType = "application/x-www-form-urlencoded";
+
+ dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
+ // summary:
+ // Adds query params discovered by the io deferred construction to the URL.
+ // Only use this for operations which are fundamentally GET-type operations.
+ if(ioArgs.query.length){
+ ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
+ ioArgs.query = null;
+ }
+ };
+
+ /*=====
+ dojo.__XhrArgs = declare(dojo.__IoArgs, {
+ // summary:
+ // In addition to the properties listed for the dojo._IoArgs type,
+ // the following properties are allowed for dojo.xhr* methods.
+ // handleAs: String?
+ // Acceptable values are: text (default), json, json-comment-optional,
+ // json-comment-filtered, javascript, xml. See `dojo/_base/xhr.contentHandlers`
+ // sync: Boolean?
+ // false is default. Indicates whether the request should
+ // be a synchronous (blocking) request.
+ // headers: Object?
+ // Additional HTTP headers to send in the request.
+ // failOk: Boolean?
+ // false is default. Indicates whether a request should be
+ // allowed to fail (and therefore no console error message in
+ // the event of a failure)
+ // contentType: String|Boolean
+ // "application/x-www-form-urlencoded" is default. Set to false to
+ // prevent a Content-Type header from being sent, or to a string
+ // to send a different Content-Type.
+ });
+ =====*/
+
+ dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
+ // summary:
+ // Deprecated. Use dojo/request instead.
+ // description:
+ // Sends an HTTP request with the given method.
+ // See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
+ // for those HTTP methods. There are also methods for "raw" PUT and POST methods
+ // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
+ // method:
+ // HTTP method to be used, such as GET, POST, PUT, DELETE. Should be uppercase.
+ // hasBody:
+ // If the request has an HTTP body, then pass true for hasBody.
+
+ var rDfd;
+ //Make the Deferred object for this xhr request.
+ var dfd = dojo._ioSetArgs(args, function(dfd){
+ rDfd && rDfd.cancel();
+ }, _deferredOk, _deferError);
+ var ioArgs = dfd.ioArgs;
+
+ //Allow for specifying the HTTP body completely.
+ if("postData" in args){
+ ioArgs.query = args.postData;
+ }else if("putData" in args){
+ ioArgs.query = args.putData;
+ }else if("rawBody" in args){
+ ioArgs.query = args.rawBody;
+ }else if((arguments.length > 2 && !hasBody) || "POST|PUT".indexOf(method.toUpperCase()) === -1){
+ //Check for hasBody being passed. If no hasBody,
+ //then only append query string if not a POST or PUT request.
+ dojo._ioAddQueryToUrl(ioArgs);
+ }
+
+ var options = {
+ method: method,
+ handleAs: "text",
+ timeout: args.timeout,
+ withCredentials: args.withCredentials,
+ ioArgs: ioArgs
+ };
+
+ if(typeof args.headers !== 'undefined'){
+ options.headers = args.headers;
+ }
+ if(typeof args.contentType !== 'undefined'){
+ if(!options.headers){
+ options.headers = {};
+ }
+ options.headers['Content-Type'] = args.contentType;
+ }
+ if(typeof ioArgs.query !== 'undefined'){
+ options.data = ioArgs.query;
+ }
+ if(typeof args.sync !== 'undefined'){
+ options.sync = args.sync;
+ }
+
+ dojo._ioNotifyStart(dfd);
+ try{
+ rDfd = _xhr(ioArgs.url, options, true);
+ }catch(e){
+ // If XHR creation fails, dojo/request/xhr throws
+ // When this happens, cancel the deferred
+ dfd.cancel();
+ return dfd;
+ }
+
+ // sync ioArgs
+ dfd.ioArgs.xhr = rDfd.response.xhr;
+
+ rDfd.then(function(){
+ dfd.resolve(dfd);
+ }).otherwise(function(error){
+ ioArgs.error = error;
+ if(error.response){
+ error.status = error.response.status;
+ error.responseText = error.response.text;
+ error.xhr = error.response.xhr;
+ }
+ dfd.reject(error);
+ });
+ return dfd; // dojo/_base/Deferred
+ };
+
+ dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP GET request to the server.
+ return dojo.xhr("GET", args); // dojo/_base/Deferred
+ };
+
+ dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP POST request to the server. In addition to the properties
+ // listed for the dojo.__XhrArgs type, the following property is allowed:
+ // postData:
+ // String. Send raw data in the body of the POST request.
+ return dojo.xhr("POST", args, true); // dojo/_base/Deferred
+ };
+
+ dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP PUT request to the server. In addition to the properties
+ // listed for the dojo.__XhrArgs type, the following property is allowed:
+ // putData:
+ // String. Send raw data in the body of the PUT request.
+ return dojo.xhr("PUT", args, true); // dojo/_base/Deferred
+ };
+
+ dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
+ // summary:
+ // Sends an HTTP DELETE request to the server.
+ return dojo.xhr("DELETE", args); // dojo/_base/Deferred
+ };
+
+ /*
+ dojo.wrapForm = function(formNode){
+ // summary:
+ // A replacement for FormBind, but not implemented yet.
+
+ // FIXME: need to think harder about what extensions to this we might
+ // want. What should we allow folks to do w/ this? What events to
+ // set/send?
+ throw new Error("dojo.wrapForm not yet implemented");
+ }
+ */
+
+ dojo._isDocumentOk = function(x){
+ return util.checkStatus(x.status);
+ };
+
+ dojo._getText = function(url){
+ var result;
+ dojo.xhrGet({url:url, sync:true, load:function(text){
+ result = text;
+ }});
+ return result;
+ };
+
+ // Add aliases for static functions to dojo.xhr since dojo.xhr is what's returned from this module
+ lang.mixin(dojo.xhr, {
+ _xhrObj: dojo._xhrObj,
+ fieldToObject: domForm.fieldToObject,
+ formToObject: domForm.toObject,
+ objectToQuery: ioq.objectToQuery,
+ formToQuery: domForm.toQuery,
+ formToJson: domForm.toJson,
+ queryToObject: ioq.queryToObject,
+ contentHandlers: handlers,
+ _ioSetArgs: dojo._ioSetArgs,
+ _ioCancelAll: dojo._ioCancelAll,
+ _ioNotifyStart: dojo._ioNotifyStart,
+ _ioWatch: dojo._ioWatch,
+ _ioAddQueryToUrl: dojo._ioAddQueryToUrl,
+ _isDocumentOk: dojo._isDocumentOk,
+ _getText: dojo._getText,
+ get: dojo.xhrGet,
+ post: dojo.xhrPost,
+ put: dojo.xhrPut,
+ del: dojo.xhrDelete // because "delete" is a reserved word
+ });
+
+ return dojo.xhr;
+});
diff --git a/src/main/resources/static/dojo/_firebug/LICENSE b/src/main/resources/static/dojo/_firebug/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..8c777a23eacd8922548d2e2262318f390e55e3e0
--- /dev/null
+++ b/src/main/resources/static/dojo/_firebug/LICENSE
@@ -0,0 +1,37 @@
+License Disclaimer:
+
+All contents of this directory are Copyright (c) the Dojo Foundation, with the
+following exceptions:
+-------------------------------------------------------------------------------
+
+firebug.html, firebug.js, errIcon.png, infoIcon.png, warningIcon.png:
+ * Copyright (c) 2006-2007, Joe Hewitt, All rights reserved.
+ Distributed under the terms of the BSD License (see below)
+
+-------------------------------------------------------------------------------
+
+Copyright (c) 2006-2007, Joe Hewitt
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Dojo Foundation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/static/dojo/_firebug/errorIcon.png b/src/main/resources/static/dojo/_firebug/errorIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d75261bb675f5f878a9ca549340d11694287ea9
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/errorIcon.png differ
diff --git a/src/main/resources/static/dojo/_firebug/firebug.css b/src/main/resources/static/dojo/_firebug/firebug.css
new file mode 100644
index 0000000000000000000000000000000000000000..2012e067481c06967cc874d9ffd8bd41ee3acf30
--- /dev/null
+++ b/src/main/resources/static/dojo/_firebug/firebug.css
@@ -0,0 +1,211 @@
+.firebug {
+ margin: 0;
+ background:#fff;
+ font-family: Lucida Grande, Tahoma, sans-serif;
+ font-size: 11px;
+ overflow: hidden;
+ border: 1px solid black;
+ position: relative;
+}
+.firebug a {
+ text-decoration: none;
+}
+.firebug a:hover {
+ text-decoration: underline;
+}
+.firebug a:visited{
+ color:#0000FF;
+}
+.firebug #firebugToolbar {
+ height: 18px;
+ line-height:18px;
+ border-top: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDShadow;
+ padding: 2px 6px;
+
+ background:#f0f0f0;
+}
+
+.firebug #firebugLog, .firebug #objectLog {
+ overflow: auto;
+ position: absolute;
+ left: 0;
+ width: 100%;
+}
+#objectLog{
+ overflow:scroll;
+ height:258px;
+}
+.firebug #firebugCommandLine {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 18px;
+ border: none;
+ border-top: 1px solid ThreeDShadow;
+}
+.firebug .logRow {
+ position: relative;
+ border-bottom: 1px solid #D7D7D7;
+ padding: 2px 4px 1px 6px;
+ background-color: #FFFFFF;
+}
+.firebug .logRow-command {
+ font-family: Monaco, monospace;
+ color: blue;
+}
+.firebug .objectBox-null {
+ padding: 0 2px;
+ border: 1px solid #666666;
+ background-color: #888888;
+ color: #FFFFFF;
+}
+.firebug .objectBox-string {
+ font-family: Monaco, monospace;
+ color: red;
+ white-space: pre;
+}
+.firebug .objectBox-number {
+ color: #000088;
+}
+.firebug .objectBox-function {
+ font-family: Monaco, monospace;
+ color: DarkGreen;
+}
+.firebug .objectBox-object {
+ color: DarkGreen;
+ font-weight: bold;
+}
+.firebug .logRow-info,
+.firebug .logRow-error,
+.firebug .logRow-warning
+ {
+ background: #00FFFF no-repeat 2px 2px;
+ padding-left: 20px;
+ padding-bottom: 3px;
+}
+.firebug .logRow-info {
+ background: #FFF url(infoIcon.png) no-repeat 2px 2px;
+ padding-left: 20px;
+ padding-bottom: 3px;
+}
+.firebug .logRow-warning {
+
+ background: #00FFFF url(warningIcon.png) no-repeat 2px 2px;
+ padding-left: 20px;
+ padding-bottom: 3px;
+}
+.firebug .logRow-error {
+ background: LightYellow url(errorIcon.png) no-repeat 2px 2px;
+ padding-left: 20px;
+ padding-bottom: 3px;
+}
+.firebug .errorMessage {
+ vertical-align: top;
+ color: #FF0000;
+}
+.firebug .objectBox-sourceLink {
+ position: absolute;
+ right: 4px;
+ top: 2px;
+ padding-left: 8px;
+ font-family: Lucida Grande, sans-serif;
+ font-weight: bold;
+ color: #0000FF;
+}
+.firebug .logRow-group {
+ background: #EEEEEE;
+ border-bottom: none;
+}
+.firebug .logGroup {
+ background: #EEEEEE;
+}
+.firebug .logGroupBox {
+ margin-left: 24px;
+ border-top: 1px solid #D7D7D7;
+ border-left: 1px solid #D7D7D7;
+}
+.firebug .selectorTag,
+.firebug .selectorId,
+.firebug .selectorClass {
+ font-family: Monaco, monospace;
+ font-weight: normal;
+}
+.firebug .selectorTag {
+ color: #0000FF;
+}
+.firebug .selectorId {
+ color: DarkBlue;
+}
+.firebug .selectorClass {
+ color: red;
+}
+.firebug .objectBox-element {
+ font-family: Monaco, monospace;
+ color: #000088;
+}
+.firebug .nodeChildren {
+ margin-left: 16px;
+}
+.firebug .nodeTag {
+ color: blue;
+}
+.firebug .nodeValue {
+ color: #FF0000;
+ font-weight: normal;
+}
+.firebug .nodeText,
+.firebug .nodeComment {
+ margin: 0 2px;
+ vertical-align: top;
+}
+.firebug .nodeText {
+ color: #333333;
+}
+.firebug .nodeComment {
+ color: DarkGreen;
+}
+.firebug .propertyNameCell {
+ vertical-align: top;
+}
+.firebug .propertyName {
+ font-weight: bold;
+}
+
+/* tabs */
+#firebugToolbar ul.tabs{
+ margin:0 !important;
+ padding:0;
+}
+#firebugToolbar ul.tabs li{
+ list-style:none;
+ background:transparent url(tab_lft_norm.png) no-repeat left;
+ line-height:18px;
+ float:left;
+ margin-left:5px;
+}
+#firebugToolbar ul.tabs li.right{
+ float:right;
+ margin-right:5px;
+ margin-left:0;
+}
+#firebugToolbar ul.tabs li.gap{
+ margin-left:20px;
+}
+#firebugToolbar .tabs a{
+ text-decoration:none;
+ background:transparent url(tab_rgt_norm.png) no-repeat right;
+ line-height:18px;
+ padding:3px 9px 4px 0px;
+ margin-left:9px;
+ color:#333333;
+}
+#firebugToolbar .tabs li:hover{
+ background:transparent url(tab_lft_over.png) no-repeat left;
+}
+#firebugToolbar .tabs a:hover{
+ text-decoration:none;
+ background:transparent url(tab_rgt_over.png) no-repeat right;
+ color:#FFFFFF;
+}
diff --git a/src/main/resources/static/dojo/_firebug/firebug.js b/src/main/resources/static/dojo/_firebug/firebug.js
new file mode 100644
index 0000000000000000000000000000000000000000..21b164dec39cc10631953aeaf459e6d4ca01a347
--- /dev/null
+++ b/src/main/resources/static/dojo/_firebug/firebug.js
@@ -0,0 +1,1191 @@
+define([
+ "../_base/kernel",
+ "require",
+ "../_base/html",
+ "../sniff",
+ "../_base/array",
+ "../_base/lang",
+ "../_base/event",
+ "../_base/unload"], function(dojo, require, html, has){
+
+ // module:
+ // dojo/_firebug/firebug
+ // summary:
+ // Firebug Lite, the baby brother to Joe Hewitt's Firebug for Mozilla Firefox
+ // description:
+ // Opens a console for logging, debugging, and error messages.
+ // Contains partial functionality to Firebug. See function list below.
+ //
+ // NOTE:
+ // Firebug is a Firefox extension created by Joe Hewitt (see license). You do not need Dojo to run Firebug.
+ // Firebug Lite is included in Dojo by permission from Joe Hewitt
+ // If you are new to Firebug, or used to the Dojo 0.4 dojo.debug, you can learn Firebug
+ // functionality by reading the function comments below or visiting http://www.getfirebug.com/docs.html
+ //
+ // NOTE:
+ // To test Firebug Lite in Firefox:
+ //
+ // - FF2: set "console = null" before loading dojo and set djConfig.isDebug=true
+ // - FF3: disable Firebug and set djConfig.isDebug=true
+ //
+ // example:
+ // Supports inline objects in object inspector window (only simple trace of dom nodes, however)
+ // | console.log("my object", {foo:"bar"})
+ // example:
+ // Option for console to open in popup window
+ // | var djConfig = {isDebug: true, popup:true };
+ // example:
+ // Option for console height (ignored for popup)
+ // | var djConfig = {isDebug: true, debugHeight:100 }
+
+
+ var isNewIE = (/Trident/.test(window.navigator.userAgent));
+ if(isNewIE){
+ // Fixing IE's console
+ // IE doesn't insert space between arguments. How annoying.
+ var calls = ["log", "info", "debug", "warn", "error"];
+ for(var i=0;i");
+ str = str.replace(/\t/g, " ");
+ logRow([str], "dir");
+ },
+
+ dirxml: function(node){
+ var html = [];
+ appendNode(node, html);
+ logRow(html, "dirxml");
+ },
+
+ group: function(){
+ // summary:
+ // collects log messages into a group, starting with this call and ending with
+ // groupEnd(). Missing collapse functionality
+ logRow(arguments, "group", pushGroup);
+ },
+
+ groupEnd: function(){
+ // summary:
+ // Closes group. See above
+ logRow(arguments, "", popGroup);
+ },
+
+ time: function(name){
+ // summary:
+ // Starts timers assigned to name given in argument. Timer stops and displays on timeEnd(title);
+ // example:
+ // | console.time("load");
+ // | console.time("myFunction");
+ // | console.timeEnd("load");
+ // | console.timeEnd("myFunction");
+ timeMap[name] = new Date().getTime();
+ },
+
+ timeEnd: function(name){
+ // summary:
+ // See above.
+ if(name in timeMap){
+ var delta = (new Date()).getTime() - timeMap[name];
+ logFormatted([name+ ":", delta+"ms"]);
+ delete timeMap[name];
+ }
+ },
+
+ count: function(name){
+ // summary:
+ // Not supported
+ if(!countMap[name]) countMap[name] = 0;
+ countMap[name]++;
+ logFormatted([name+": "+countMap[name]]);
+ },
+
+ trace: function(_value){
+ var stackAmt = _value || 3;
+ var f = console.trace.caller; //function that called trace
+ console.log(">>> console.trace(stack)");
+ for(var i=0;i=0&&s.href){
+ var h=s.href.replace(/(&|%5C?)forceReload=\d+/,'');
+ s.href=h+(h.indexOf('?')>=0?'&':'?')+'forceReload='+new Date().valueOf();
+ }
+ }
+ }
+ };
+
+ // ***************************************************************************
+
+ function toggleConsole(forceOpen){
+ frameVisible = forceOpen || !frameVisible;
+ if(consoleFrame){
+ consoleFrame.style.display = frameVisible ? "block" : "none";
+ }
+ }
+
+ function focusCommandLine(){
+ toggleConsole(true);
+ if(commandLine){
+ commandLine.focus();
+ }
+ }
+
+ function openWin(x,y,w,h){
+ var win = window.open("","_firebug","status=0,menubar=0,resizable=1,top="+y+",left="+x+",width="+w+",height="+h+",scrollbars=1,addressbar=0");
+ if(!win){
+ var msg = "Firebug Lite could not open a pop-up window, most likely because of a blocker.\n" +
+ "Either enable pop-ups for this domain, or change the djConfig to popup=false.";
+ alert(msg);
+ }
+ createResizeHandler(win);
+ var newDoc=win.document;
+ //Safari needs an HTML height
+ var HTMLstring= 'Firebug Lite\n' +
+ '\n' +
+ '' +
+ '';
+
+ newDoc.write(HTMLstring);
+ newDoc.close();
+ return win;
+ }
+
+ function createResizeHandler(wn){
+ // summary:
+ // Creates handle for onresize window. Called from script in popup's body tag (so that it will work with IE).
+ //
+
+ var d = new Date();
+ d.setTime(d.getTime()+(60*24*60*60*1000)); // 60 days
+ d = d.toUTCString();
+
+ var dc = wn.document,
+ getViewport;
+
+ if (wn.innerWidth){
+ getViewport = function(){
+ return{w:wn.innerWidth, h:wn.innerHeight};
+ };
+ }else if (dc.documentElement && dc.documentElement.clientWidth){
+ getViewport = function(){
+ return{w:dc.documentElement.clientWidth, h:dc.documentElement.clientHeight};
+ };
+ }else if (dc.body){
+ getViewport = function(){
+ return{w:dc.body.clientWidth, h:dc.body.clientHeight};
+ };
+ }
+
+
+ window.onFirebugResize = function(){
+
+ //resize the height of the console log body
+ layout(getViewport().h);
+
+ clearInterval(wn._firebugWin_resize);
+ wn._firebugWin_resize = setTimeout(function(){
+ var x = wn.screenLeft,
+ y = wn.screenTop,
+ w = wn.outerWidth || wn.document.body.offsetWidth,
+ h = wn.outerHeight || wn.document.body.offsetHeight;
+
+ document.cookie = "_firebugPosition=" + [x,y,w,h].join(",") + "; expires="+d+"; path=/";
+
+ }, 5000); //can't capture window.onMove - long timeout gives better chance of capturing a resize, then the move
+
+ };
+ }
+
+
+ /*****************************************************************************/
+
+
+ function createFrame(){
+ if(consoleFrame){
+ return;
+ }
+ toggleConsole(true);
+ if(dojo.config.popup){
+ var containerHeight = "100%";
+ var cookieMatch = document.cookie.match(/(?:^|; )_firebugPosition=([^;]*)/);
+ var p = cookieMatch ? cookieMatch[1].split(",") : [2,2,320,480];
+
+ _firebugWin = openWin(p[0],p[1],p[2],p[3]); // global
+ _firebugDoc = _firebugWin.document; // global
+
+ dojo.config.debugContainerId = 'fb';
+
+ // connecting popup
+ _firebugWin.console = window.console;
+ _firebugWin.dojo = window.dojo;
+ }else{
+ _firebugDoc = document;
+ containerHeight = (dojo.config.debugHeight || 300) + "px";
+ }
+
+ var styleElement = _firebugDoc.createElement("link");
+ styleElement.href = require.toUrl("./firebug.css");
+ styleElement.rel = "stylesheet";
+ styleElement.type = "text/css";
+ var styleParent = _firebugDoc.getElementsByTagName("head");
+ if(styleParent){
+ styleParent = styleParent[0];
+ }
+ if(!styleParent){
+ styleParent = _firebugDoc.getElementsByTagName("html")[0];
+ }
+ if(has("ie")){
+ window.setTimeout(function(){ styleParent.appendChild(styleElement); }, 0);
+ }else{
+ styleParent.appendChild(styleElement);
+ }
+
+ if(dojo.config.debugContainerId){
+ consoleFrame = _firebugDoc.getElementById(dojo.config.debugContainerId);
+ }
+ if(!consoleFrame){
+ consoleFrame = _firebugDoc.createElement("div");
+ _firebugDoc.body.appendChild(consoleFrame);
+ }
+ consoleFrame.className += " firebug";
+ consoleFrame.id = "firebug";
+ consoleFrame.style.height = containerHeight;
+ consoleFrame.style.display = (frameVisible ? "block" : "none");
+
+ var buildLink = function(label, title, method, _class){
+ return '
'
+ ];
+
+ logRow(html, "error");
+ }
+
+
+ //After converting to div instead of iframe, now getting two keydowns right away in IE 6.
+ //Make sure there is a little bit of delay.
+ var onKeyDownTime = new Date().getTime();
+
+ function onKeyDown(event){
+ var timestamp = (new Date()).getTime();
+ if(timestamp > onKeyDownTime + 200){
+ event = dojo.fixEvent(event);
+ var keys = dojo.keys;
+ var ekc = event.keyCode;
+ onKeyDownTime = timestamp;
+ if(ekc == keys.F12){
+ toggleConsole();
+ }else if(
+ (ekc == keys.NUMPAD_ENTER || ekc == 76) &&
+ event.shiftKey &&
+ (event.metaKey || event.ctrlKey)
+ ){
+ focusCommandLine();
+ }else{
+ return;
+ }
+ cancelEvent(event);
+ }
+ }
+
+ function onCommandLineKeyDown(e){
+ var dk = dojo.keys;
+ if(e.keyCode == 13 && commandLine.value){
+ addToHistory(commandLine.value);
+ evalCommandLine();
+ }else if(e.keyCode == 27){
+ commandLine.value = "";
+ }else if(e.keyCode == dk.UP_ARROW || e.charCode == dk.UP_ARROW){
+ navigateHistory("older");
+ }else if(e.keyCode == dk.DOWN_ARROW || e.charCode == dk.DOWN_ARROW){
+ navigateHistory("newer");
+ }else if(e.keyCode == dk.HOME || e.charCode == dk.HOME){
+ historyPosition = 1;
+ navigateHistory("older");
+ }else if(e.keyCode == dk.END || e.charCode == dk.END){
+ historyPosition = 999999;
+ navigateHistory("newer");
+ }
+ }
+
+ var historyPosition = -1;
+ var historyCommandLine = null;
+
+ function addToHistory(value){
+ var history = cookie("firebug_history");
+ history = (history) ? dojo.fromJson(history) : [];
+ var pos = dojo.indexOf(history, value);
+ if (pos != -1){
+ history.splice(pos, 1);
+ }
+ history.push(value);
+ cookie("firebug_history", dojo.toJson(history), 30);
+ while(history.length && !cookie("firebug_history")){
+ history.shift();
+ cookie("firebug_history", dojo.toJson(history), 30);
+ }
+ historyCommandLine = null;
+ historyPosition = -1;
+ }
+
+ function navigateHistory(direction){
+ var history = cookie("firebug_history");
+ history = (history) ? dojo.fromJson(history) : [];
+ if(!history.length){
+ return;
+ }
+
+ if(historyCommandLine === null){
+ historyCommandLine = commandLine.value;
+ }
+
+ if(historyPosition == -1){
+ historyPosition = history.length;
+ }
+
+ if(direction == "older"){
+ --historyPosition;
+ if(historyPosition < 0){
+ historyPosition = 0;
+ }
+ }else if(direction == "newer"){
+ ++historyPosition;
+ if(historyPosition > history.length){
+ historyPosition = history.length;
+ }
+ }
+
+ if(historyPosition == history.length){
+ commandLine.value = historyCommandLine;
+ historyCommandLine = null;
+ }else{
+ commandLine.value = history[historyPosition];
+ }
+ }
+
+ function cookie(name, value){
+ var c = document.cookie;
+ if(arguments.length == 1){
+ var matches = c.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
+ return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
+ }else{
+ var d = new Date();
+ d.setMonth(d.getMonth()+1);
+ document.cookie = name + "=" + encodeURIComponent(value) + ((d.toUtcString) ? "; expires=" + d.toUTCString() : "");
+ }
+ }
+
+ function isArray(it){
+ return it && it instanceof Array || typeof it == "array";
+ }
+
+ //***************************************************************************************************
+ // Print Object Helpers
+ function objectLength(o){
+ var cnt = 0;
+ for(var nm in o){
+ cnt++;
+ }
+ return cnt;
+ }
+
+ function printObject(o, i, txt, used){
+ // Recursively trace object, indenting to represent depth for display in object inspector
+ var ind = " \t";
+ txt = txt || "";
+ i = i || ind;
+ used = used || [];
+ var opnCls;
+
+ if(o && o.nodeType == 1){
+ var html = [];
+ appendNode(o, html);
+ return html.join("");
+ }
+
+ var br=",\n", cnt = 0, length = objectLength(o);
+
+ if(o instanceof Date){
+ return i + o.toString() + br;
+ }
+ looking:
+ for(var nm in o){
+ cnt++;
+ if(cnt==length){br = "\n";}
+ if(o[nm] === window || o[nm] === document){
+ // do nothing
+ }else if(o[nm] === null){
+ txt += i+nm + " : NULL" + br;
+ }else if(o[nm] && o[nm].nodeType){
+ if(o[nm].nodeType == 1){
+ //txt += i+nm + " : < "+o[nm].tagName+" id=\""+ o[nm].id+"\" />" + br;
+ }else if(o[nm].nodeType == 3){
+ txt += i+nm + " : [ TextNode "+o[nm].data + " ]" + br;
+ }
+
+ }else if(typeof o[nm] == "object" && (o[nm] instanceof String || o[nm] instanceof Number || o[nm] instanceof Boolean)){
+ txt += i+nm + " : " + o[nm] + "," + br;
+
+ }else if(o[nm] instanceof Date){
+ txt += i+nm + " : " + o[nm].toString() + br;
+
+ }else if(typeof(o[nm]) == "object" && o[nm]){
+ for(var j = 0, seen; seen = used[j]; j++){
+ if(o[nm] === seen){
+ txt += i+nm + " : RECURSION" + br;
+ continue looking;
+ }
+ }
+ used.push(o[nm]);
+
+ opnCls = (isArray(o[nm]))?["[","]"]:["{","}"];
+ txt += i+nm +" : " + opnCls[0] + "\n";//non-standard break, (no comma)
+ txt += printObject(o[nm], i+ind, "", used);
+ txt += i + opnCls[1] + br;
+
+ }else if(typeof o[nm] == "undefined"){
+ txt += i+nm + " : undefined" + br;
+ }else if(nm == "toString" && typeof o[nm] == "function"){
+ var toString = o[nm]();
+ if(typeof toString == "string" && toString.match(/function ?(.*?)\(/)){
+ toString = escapeHTML(getObjectAbbr(o[nm]));
+ }
+ txt += i+nm +" : " + toString + br;
+ }else{
+ txt += i+nm +" : "+ escapeHTML(getObjectAbbr(o[nm])) + br;
+ }
+ }
+ return txt;
+ }
+
+ function getObjectAbbr(obj){
+ // Gets an abbreviation of an object for display in log
+ // X items in object, including id
+ // X items in an array
+ // TODO: Firebug Sr. actually goes by char count
+ var isError = (obj instanceof Error);
+ if(obj.nodeType == 1){
+ return escapeHTML('< '+obj.tagName.toLowerCase()+' id=\"'+ obj.id+ '\" />');
+ }
+ if(obj.nodeType == 3){
+ return escapeHTML('[TextNode: "'+obj.nodeValue+'"]');
+ }
+ var nm = (obj && (obj.id || obj.name || obj.ObjectID || obj.widgetId));
+ if(!isError && nm){ return "{"+nm+"}"; }
+
+ var obCnt = 2;
+ var arCnt = 4;
+ var cnt = 0;
+
+ if(isError){
+ nm = "[ Error: "+(obj.message || obj.description || obj)+" ]";
+ }else if(isArray(obj)){
+ nm = "[" + obj.slice(0,arCnt).join(",");
+ if(obj.length > arCnt){
+ nm += " ... ("+obj.length+" items)";
+ }
+ nm += "]";
+ }else if(typeof obj == "function"){
+ nm = obj + "";
+ var reg = /function\s*([^\(]*)(\([^\)]*\))[^\{]*\{/;
+ var m = reg.exec(nm);
+ if(m){
+ if(!m[1]){
+ m[1] = "function";
+ }
+ nm = m[1] + m[2];
+ }else{
+ nm = "function()";
+ }
+ }else if(typeof obj != "object" || typeof obj == "string"){
+ nm = obj + "";
+ }else{
+ nm = "{";
+ for(var i in obj){
+ cnt++;
+ if(cnt > obCnt){ break; }
+ nm += i+":"+escapeHTML(obj[i])+" ";
+ }
+ nm+="}";
+ }
+
+ return nm;
+ }
+
+ //*************************************************************************************
+
+ //window.onerror = onError;
+
+ addEvent(document, has("ie") || has("safari") ? "keydown" : "keypress", onKeyDown);
+
+ if( (document.documentElement.getAttribute("debug") == "true")||
+ (dojo.config.isDebug)
+ ){
+ toggleConsole(true);
+ }
+
+ dojo.addOnWindowUnload(function(){
+ // Erase the globals and event handlers I created, to prevent spurious leak warnings
+ removeEvent(document, has("ie") || has("safari") ? "keydown" : "keypress", onKeyDown);
+ window.onFirebugResize = null;
+ window.console = null;
+ });
+
+});
diff --git a/src/main/resources/static/dojo/_firebug/infoIcon.png b/src/main/resources/static/dojo/_firebug/infoIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..da1e5334c19375c7855e04792661bf2cc15b7e14
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/infoIcon.png differ
diff --git a/src/main/resources/static/dojo/_firebug/tab_lft_norm.png b/src/main/resources/static/dojo/_firebug/tab_lft_norm.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0479a28a551462b2c3ad3066582fe94e2d07a25
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/tab_lft_norm.png differ
diff --git a/src/main/resources/static/dojo/_firebug/tab_lft_over.png b/src/main/resources/static/dojo/_firebug/tab_lft_over.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f36cca926242ebdd8eda81d60ee859299f55b19
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/tab_lft_over.png differ
diff --git a/src/main/resources/static/dojo/_firebug/tab_rgt_norm.png b/src/main/resources/static/dojo/_firebug/tab_rgt_norm.png
new file mode 100644
index 0000000000000000000000000000000000000000..464af3ef17bff273ffd6d42b45e17e0c384a3cfb
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/tab_rgt_norm.png differ
diff --git a/src/main/resources/static/dojo/_firebug/tab_rgt_over.png b/src/main/resources/static/dojo/_firebug/tab_rgt_over.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bc2cd07569b762a9dabd5ef9c46e20b478d3323
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/tab_rgt_over.png differ
diff --git a/src/main/resources/static/dojo/_firebug/warningIcon.png b/src/main/resources/static/dojo/_firebug/warningIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..de51084e8489f498b89dd9a59d82eb9564b7d050
Binary files /dev/null and b/src/main/resources/static/dojo/_firebug/warningIcon.png differ
diff --git a/src/main/resources/static/dojo/aspect.js b/src/main/resources/static/dojo/aspect.js
new file mode 100644
index 0000000000000000000000000000000000000000..391f166dcea5f9cd1855b3b8d4f7c75e734534e7
--- /dev/null
+++ b/src/main/resources/static/dojo/aspect.js
@@ -0,0 +1,223 @@
+define([], function(){
+
+ // module:
+ // dojo/aspect
+
+ "use strict";
+ var undefined, nextId = 0;
+ function advise(dispatcher, type, advice, receiveArguments){
+ var previous = dispatcher[type];
+ var around = type == "around";
+ var signal;
+ if(around){
+ var advised = advice(function(){
+ return previous.advice(this, arguments);
+ });
+ signal = {
+ remove: function(){
+ if(advised){
+ advised = dispatcher = advice = null;
+ }
+ },
+ advice: function(target, args){
+ return advised ?
+ advised.apply(target, args) : // called the advised function
+ previous.advice(target, args); // cancelled, skip to next one
+ }
+ };
+ }else{
+ // create the remove handler
+ signal = {
+ remove: function(){
+ if(signal.advice){
+ var previous = signal.previous;
+ var next = signal.next;
+ if(!next && !previous){
+ delete dispatcher[type];
+ }else{
+ if(previous){
+ previous.next = next;
+ }else{
+ dispatcher[type] = next;
+ }
+ if(next){
+ next.previous = previous;
+ }
+ }
+
+ // remove the advice to signal that this signal has been removed
+ dispatcher = advice = signal.advice = null;
+ }
+ },
+ id: nextId++,
+ advice: advice,
+ receiveArguments: receiveArguments
+ };
+ }
+ if(previous && !around){
+ if(type == "after"){
+ // add the listener to the end of the list
+ // note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bug
+ while(previous.next && (previous = previous.next)){}
+ previous.next = signal;
+ signal.previous = previous;
+ }else if(type == "before"){
+ // add to beginning
+ dispatcher[type] = signal;
+ signal.next = previous;
+ previous.previous = signal;
+ }
+ }else{
+ // around or first one just replaces
+ dispatcher[type] = signal;
+ }
+ return signal;
+ }
+ function aspect(type){
+ return function(target, methodName, advice, receiveArguments){
+ var existing = target[methodName], dispatcher;
+ if(!existing || existing.target != target){
+ // no dispatcher in place
+ target[methodName] = dispatcher = function(){
+ var executionId = nextId;
+ // before advice
+ var args = arguments;
+ var before = dispatcher.before;
+ while(before){
+ args = before.advice.apply(this, args) || args;
+ before = before.next;
+ }
+ // around advice
+ if(dispatcher.around){
+ var results = dispatcher.around.advice(this, args);
+ }
+ // after advice
+ var after = dispatcher.after;
+ while(after && after.id < executionId){
+ if(after.receiveArguments){
+ var newResults = after.advice.apply(this, args);
+ // change the return value only if a new value was returned
+ results = newResults === undefined ? results : newResults;
+ }else{
+ results = after.advice.call(this, results, args);
+ }
+ after = after.next;
+ }
+ return results;
+ };
+ if(existing){
+ dispatcher.around = {advice: function(target, args){
+ return existing.apply(target, args);
+ }};
+ }
+ dispatcher.target = target;
+ }
+ var results = advise((dispatcher || existing), type, advice, receiveArguments);
+ advice = null;
+ return results;
+ };
+ }
+
+ // TODOC: after/before/around return object
+
+ var after = aspect("after");
+ /*=====
+ after = function(target, methodName, advice, receiveArguments){
+ // summary:
+ // The "after" export of the aspect module is a function that can be used to attach
+ // "after" advice to a method. This function will be executed after the original method
+ // is executed. By default the function will be called with a single argument, the return
+ // value of the original method, or the the return value of the last executed advice (if a previous one exists).
+ // The fourth (optional) argument can be set to true to so the function receives the original
+ // arguments (from when the original method was called) rather than the return value.
+ // If there are multiple "after" advisors, they are executed in the order they were registered.
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called after the original method
+ // receiveArguments: Boolean?
+ // If this is set to true, the advice function receives the original arguments (from when the original mehtod
+ // was called) rather than the return value of the original/previous method.
+ // returns:
+ // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will
+ // stop the advice function from being executed.
+ };
+ =====*/
+
+ var before = aspect("before");
+ /*=====
+ before = function(target, methodName, advice){
+ // summary:
+ // The "before" export of the aspect module is a function that can be used to attach
+ // "before" advice to a method. This function will be executed before the original method
+ // is executed. This function will be called with the arguments used to call the method.
+ // This function may optionally return an array as the new arguments to use to call
+ // the original method (or the previous, next-to-execute before advice, if one exists).
+ // If the before method doesn't return anything (returns undefined) the original arguments
+ // will be preserved.
+ // If there are multiple "before" advisors, they are executed in the reverse order they were registered.
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called before the original method
+ };
+ =====*/
+
+ var around = aspect("around");
+ /*=====
+ around = function(target, methodName, advice){
+ // summary:
+ // The "around" export of the aspect module is a function that can be used to attach
+ // "around" advice to a method. The advisor function is immediately executed when
+ // the around() is called, is passed a single argument that is a function that can be
+ // called to continue execution of the original method (or the next around advisor).
+ // The advisor function should return a function, and this function will be called whenever
+ // the method is called. It will be called with the arguments used to call the method.
+ // Whatever this function returns will be returned as the result of the method call (unless after advise changes it).
+ // example:
+ // If there are multiple "around" advisors, the most recent one is executed first,
+ // which can then delegate to the next one and so on. For example:
+ // | around(obj, "foo", function(originalFoo){
+ // | return function(){
+ // | var start = new Date().getTime();
+ // | var results = originalFoo.apply(this, arguments); // call the original
+ // | var end = new Date().getTime();
+ // | console.log("foo execution took " + (end - start) + " ms");
+ // | return results;
+ // | };
+ // | });
+ // target: Object
+ // This is the target object
+ // methodName: String
+ // This is the name of the method to attach to.
+ // advice: Function
+ // This is function to be called around the original method
+ };
+ =====*/
+
+ return {
+ // summary:
+ // provides aspect oriented programming functionality, allowing for
+ // one to add before, around, or after advice on existing methods.
+ // example:
+ // | define(["dojo/aspect"], function(aspect){
+ // | var signal = aspect.after(targetObject, "methodName", function(someArgument){
+ // | this will be called when targetObject.methodName() is called, after the original function is called
+ // | });
+ //
+ // example:
+ // The returned signal object can be used to cancel the advice.
+ // | signal.remove(); // this will stop the advice from being executed anymore
+ // | aspect.before(targetObject, "methodName", function(someArgument){
+ // | // this will be called when targetObject.methodName() is called, before the original function is called
+ // | });
+
+ before: before,
+ around: around,
+ after: after
+ };
+});
diff --git a/src/main/resources/static/dojo/back.js b/src/main/resources/static/dojo/back.js
new file mode 100644
index 0000000000000000000000000000000000000000..75a2e8de3cb60cee2b7361304ea6033d7958e169
--- /dev/null
+++ b/src/main/resources/static/dojo/back.js
@@ -0,0 +1,397 @@
+define(["./_base/config", "./_base/lang", "./sniff", "./dom", "./dom-construct", "./_base/window", "require"],
+ function(config, lang, has, dom, domConstruct, baseWindow, require){
+ // module:
+ // dojo/back
+
+ var back = {
+ // summary:
+ // Browser history management resources
+ };
+ has("extend-dojo") && lang.setObject("dojo.back", back);
+
+ // everyone deals with encoding the hash slightly differently
+
+ var getHash = back.getHash = function(){
+ var h = window.location.hash;
+ if(h.charAt(0) == "#"){ h = h.substring(1); }
+ return has("mozilla") ? h : decodeURIComponent(h);
+ },
+
+ setHash = back.setHash = function(h){
+ if(!h){ h = ""; }
+ window.location.hash = encodeURIComponent(h);
+ historyCounter = history.length;
+ };
+
+ var initialHref = (typeof(window) !== "undefined") ? window.location.href : "";
+ var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
+ var initialState = null;
+
+ var locationTimer = null;
+ var bookmarkAnchor = null;
+ var historyIframe = null;
+ var forwardStack = [];
+ var historyStack = [];
+ var moveForward = false;
+ var changingUrl = false;
+ var historyCounter;
+
+ function handleBackButton(){
+ // summary:
+ // private method. Do not call this directly.
+
+ //The "current" page is always at the top of the history stack.
+ var current = historyStack.pop();
+ if(!current){ return; }
+ var last = historyStack[historyStack.length-1];
+ if(!last && historyStack.length == 0){
+ last = initialState;
+ }
+ if(last){
+ if(last.kwArgs["back"]){
+ last.kwArgs["back"]();
+ }else if(last.kwArgs["backButton"]){
+ last.kwArgs["backButton"]();
+ }else if(last.kwArgs["handle"]){
+ last.kwArgs.handle("back");
+ }
+ }
+ forwardStack.push(current);
+ }
+
+ back.goBack = handleBackButton;
+
+ function handleForwardButton(){
+ // summary:
+ // private method. Do not call this directly.
+ var last = forwardStack.pop();
+ if(!last){ return; }
+ if(last.kwArgs["forward"]){
+ last.kwArgs.forward();
+ }else if(last.kwArgs["forwardButton"]){
+ last.kwArgs.forwardButton();
+ }else if(last.kwArgs["handle"]){
+ last.kwArgs.handle("forward");
+ }
+ historyStack.push(last);
+ }
+
+ back.goForward = handleForwardButton;
+
+ function createState(url, args, hash){
+ // summary:
+ // private method. Do not call this directly.
+ return {"url": url, "kwArgs": args, "urlHash": hash}; //Object
+ }
+
+ function getUrlQuery(url){
+ // summary:
+ // private method. Do not call this directly.
+ var segments = url.split("?");
+ if(segments.length < 2){
+ return null; //null
+ }
+ else{
+ return segments[1]; //String
+ }
+ }
+
+ function loadIframeHistory(){
+ // summary:
+ // private method. Do not call this directly.
+ var url = (config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html")) + "?" + (new Date()).getTime();
+ moveForward = true;
+ if(historyIframe){
+ has("webkit") ? historyIframe.location = url : window.frames[historyIframe.name].location = url;
+ }else{
+ //console.warn("dojo/back: Not initialised. You need to call back.init() from a `
+ // into a function
+ // script: DOMNode
+ // The `` node,
+ // calls require() to load the specified modules and (asynchronously) assign them to the specified global
+ // variables, and returns a Promise for when that operation completes.
+ //
+ // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
+
+ var hash = myEval("{" + script.innerHTML + "}"), // can't use dojo/json::parse() because maybe no quotes
+ vars = [],
+ mids = [],
+ d = new Deferred();
+
+ var contextRequire = (options && options.contextRequire) || require;
+
+ for(var name in hash){
+ vars.push(name);
+ mids.push(hash[name]);
+ }
+
+ contextRequire(mids, function(){
+ for(var i = 0; i < vars.length; i++){
+ dlang.setObject(vars[i], arguments[i]);
+ }
+ d.resolve(arguments);
+ });
+
+ return d.promise;
+ },
+
+ _scanAmd: function(root, options){
+ // summary:
+ // Scans the DOM for any declarative requires and returns their values.
+ // description:
+ // Looks for `` node, calls require() to load the
+ // specified modules and (asynchronously) assign them to the specified global variables,
+ // and returns a Promise for when those operations complete.
+ // root: DomNode
+ // The node to base the scan from.
+ // options: Object?
+ // a kwArgs options object, see parse() for details
+
+ // Promise that resolves when all the
+ //
+ return new NodeList(); // dojo/NodeList
+ };
+ =====*/
+
+ // the query that is returned from this module is slightly different than dojo.query,
+ // because dojo.query has to maintain backwards compatibility with returning a
+ // true array which has performance problems. The query returned from the module
+ // does not use true arrays, but rather inherits from Array, making it much faster to
+ // instantiate.
+ dojo.query = queryForEngine(defaultEngine, function(array){
+ // call it without the new operator to invoke the back-compat behavior that returns a true array
+ return NodeList(array); // dojo/NodeList
+ });
+
+ query.load = function(id, parentRequire, loaded){
+ // summary:
+ // can be used as AMD plugin to conditionally load new query engine
+ // example:
+ // | require(["dojo/query!custom"], function(qsa){
+ // | // loaded selector/custom.js as engine
+ // | qsa("#foobar").forEach(...);
+ // | });
+ loader.load(id, parentRequire, function(engine){
+ loaded(queryForEngine(engine, NodeList));
+ });
+ };
+
+ dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){
+ return new NodeList(query.filter(nodes, selector, root));
+ };
+ dojo.NodeList = query.NodeList = NodeList;
+ return query;
+});
diff --git a/src/main/resources/static/dojo/ready.js b/src/main/resources/static/dojo/ready.js
new file mode 100644
index 0000000000000000000000000000000000000000..975719b8b5d7a30f46eaab12270b4631b6d469a5
--- /dev/null
+++ b/src/main/resources/static/dojo/ready.js
@@ -0,0 +1,153 @@
+define(["./_base/kernel", "./has", "require", "./has!host-browser?./domReady", "./_base/lang"], function(dojo, has, require, domReady, lang){
+ // module:
+ // dojo/ready
+ // note:
+ // This module should be unnecessary in dojo 2.0
+
+ var
+ // truthy if DOMContentLoaded or better (e.g., window.onload fired) has been achieved
+ isDomReady = 0,
+
+ // The queue of functions waiting to execute as soon as dojo.ready conditions satisfied
+ loadQ = [],
+
+ // prevent recursion in onLoad
+ onLoadRecursiveGuard = 0,
+
+ handleDomReady = function(){
+ isDomReady = 1;
+ dojo._postLoad = dojo.config.afterOnLoad = true;
+ onEvent();
+ },
+
+ onEvent = function(){
+ // Called when some state changes:
+ // - dom ready
+ // - dojo/domReady has finished processing everything in its queue
+ // - task added to loadQ
+ // - require() has finished loading all currently requested modules
+ //
+ // Run the functions queued with dojo.ready if appropriate.
+
+
+ //guard against recursions into this function
+ if(onLoadRecursiveGuard){
+ return;
+ }
+ onLoadRecursiveGuard = 1;
+
+ // Run tasks in queue if require() is finished loading modules, the dom is ready, and there are no
+ // pending tasks registered via domReady().
+ // The last step is necessary so that a user defined dojo.ready() callback is delayed until after the
+ // domReady() calls inside of dojo. Failure can be seen on dijit/tests/robot/Dialog_ally.html on IE8
+ // because the dijit/focus.js domReady() callback doesn't execute until after the test starts running.
+ while(isDomReady && (!domReady || domReady._Q.length == 0) && (require.idle ? require.idle() : true) && loadQ.length){
+ var f = loadQ.shift();
+ try{
+ f();
+ }catch(e){
+ // force the dojo.js on("error") handler do display the message
+ e.info = e.message;
+ if(require.signal){
+ require.signal("error", e);
+ }else{
+ throw e;
+ }
+ }
+ }
+
+ onLoadRecursiveGuard = 0;
+ };
+
+ // Check if we should run the next queue operation whenever require() finishes loading modules or domReady
+ // finishes processing it's queue.
+ require.on && require.on("idle", onEvent);
+ if(domReady){
+ domReady._onQEmpty = onEvent;
+ }
+
+ var ready = dojo.ready = dojo.addOnLoad = function(priority, context, callback){
+ // summary:
+ // Add a function to execute on DOM content loaded and all requested modules have arrived and been evaluated.
+ // In most cases, the `domReady` plug-in should suffice and this method should not be needed.
+ //
+ // When called in a non-browser environment, just checks that all requested modules have arrived and been
+ // evaluated.
+ // priority: Integer?
+ // The order in which to exec this callback relative to other callbacks, defaults to 1000
+ // context: Object?|Function
+ // The context in which to run execute callback, or a callback if not using context
+ // callback: Function?
+ // The function to execute.
+ //
+ // example:
+ // Simple DOM and Modules ready syntax
+ // | require(["dojo/ready"], function(ready){
+ // | ready(function(){ alert("Dom ready!"); });
+ // | });
+ //
+ // example:
+ // Using a priority
+ // | require(["dojo/ready"], function(ready){
+ // | ready(2, function(){ alert("low priority ready!"); })
+ // | });
+ //
+ // example:
+ // Using context
+ // | require(["dojo/ready"], function(ready){
+ // | ready(foo, function(){
+ // | // in here, this == foo
+ // | });
+ // | });
+ //
+ // example:
+ // Using dojo/hitch style args:
+ // | require(["dojo/ready"], function(ready){
+ // | var foo = { dojoReady: function(){ console.warn(this, "dojo dom and modules ready."); } };
+ // | ready(foo, "dojoReady");
+ // | });
+
+ var hitchArgs = lang._toArray(arguments);
+ if(typeof priority != "number"){
+ callback = context;
+ context = priority;
+ priority = 1000;
+ }else{
+ hitchArgs.shift();
+ }
+ callback = callback ?
+ lang.hitch.apply(dojo, hitchArgs) :
+ function(){
+ context();
+ };
+ callback.priority = priority;
+ for(var i = 0; i < loadQ.length && priority >= loadQ[i].priority; i++){}
+ loadQ.splice(i, 0, callback);
+ onEvent();
+ };
+
+ has.add("dojo-config-addOnLoad", 1);
+ if(has("dojo-config-addOnLoad")){
+ var dca = dojo.config.addOnLoad;
+ if(dca){
+ ready[(lang.isArray(dca) ? "apply" : "call")](dojo, dca);
+ }
+ }
+
+ if(has("dojo-sync-loader") && dojo.config.parseOnLoad && !dojo.isAsync){
+ ready(99, function(){
+ if(!dojo.parser){
+ dojo.deprecated("Add explicit require(['dojo/parser']);", "", "2.0");
+ require(["dojo/parser"]);
+ }
+ });
+ }
+
+ if(domReady){
+ domReady(handleDomReady);
+ }else{
+ handleDomReady();
+ }
+
+ return ready;
+});
diff --git a/src/main/resources/static/dojo/regexp.js b/src/main/resources/static/dojo/regexp.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2b6cb664c0a18af5dca5f8ebeb348c9fbae3da9
--- /dev/null
+++ b/src/main/resources/static/dojo/regexp.js
@@ -0,0 +1,70 @@
+define(["./_base/kernel", "./_base/lang"], function(dojo, lang){
+
+// module:
+// dojo/regexp
+
+var regexp = {
+ // summary:
+ // Regular expressions and Builder resources
+};
+lang.setObject("dojo.regexp", regexp);
+
+regexp.escapeString = function(/*String*/str, /*String?*/except){
+ // summary:
+ // Adds escape sequences for special characters in regular expressions
+ // except:
+ // a String with special characters to be left unescaped
+
+ return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
+ if(except && except.indexOf(ch) != -1){
+ return ch;
+ }
+ return "\\" + ch;
+ }); // String
+};
+
+regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
+ // summary:
+ // Builds a regular expression that groups subexpressions
+ // description:
+ // A utility function used by some of the RE generators. The
+ // subexpressions are constructed by the function, re, in the second
+ // parameter. re builds one subexpression for each elem in the array
+ // a, in the first parameter. Returns a string for a regular
+ // expression that groups all the subexpressions.
+ // arr:
+ // A single value or an array of values.
+ // re:
+ // A function. Takes one parameter and converts it to a regular
+ // expression.
+ // nonCapture:
+ // If true, uses non-capturing match, otherwise matches are retained
+ // by regular expression. Defaults to false
+
+ // case 1: a is a single value.
+ if(!(arr instanceof Array)){
+ return re(arr); // String
+ }
+
+ // case 2: a is an array
+ var b = [];
+ for(var i = 0; i < arr.length; i++){
+ // convert each elem to a RE
+ b.push(re(arr[i]));
+ }
+
+ // join the REs as alternatives in a RE group.
+ return regexp.group(b.join("|"), nonCapture); // String
+};
+
+regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
+ // summary:
+ // adds group match to expression
+ // nonCapture:
+ // If true, uses non-capturing match, otherwise matches are retained
+ // by regular expression.
+ return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
+};
+
+return regexp;
+});
diff --git a/src/main/resources/static/dojo/request.js b/src/main/resources/static/dojo/request.js
new file mode 100644
index 0000000000000000000000000000000000000000..bdb85711cab0d3309a2e6b8381e611b6ee6a1066
--- /dev/null
+++ b/src/main/resources/static/dojo/request.js
@@ -0,0 +1,81 @@
+define([
+ './request/default!'/*=====,
+ './_base/declare',
+ './promise/Promise' =====*/
+], function(request/*=====, declare, Promise =====*/){
+ /*=====
+ request = function(url, options){
+ // summary:
+ // Send a request using the default transport for the current platform.
+ // url: String
+ // The URL to request.
+ // options: dojo/request.__Options?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ request.__Promise = declare(Promise, {
+ // response: dojo/promise/Promise
+ // A promise resolving to an object representing
+ // the response from the server.
+ });
+ request.__BaseOptions = declare(null, {
+ // query: String|Object?
+ // Query parameters to append to the URL.
+ // data: String|Object?
+ // Data to transfer. This is ignored for GET and DELETE
+ // requests.
+ // preventCache: Boolean?
+ // Whether to append a cache-busting parameter to the URL.
+ // timeout: Integer?
+ // Milliseconds to wait for the response. If this time
+ // passes, the then the promise is rejected.
+ // handleAs: String?
+ // How to handle the response from the server. Default is
+ // 'text'. Other values are 'json', 'javascript', and 'xml'.
+ });
+ request.__MethodOptions = declare(null, {
+ // method: String?
+ // The HTTP method to use to make the request. Must be
+ // uppercase.
+ });
+ request.__Options = declare([request.__BaseOptions, request.__MethodOptions]);
+
+ request.get = function(url, options){
+ // summary:
+ // Send an HTTP GET request using the default transport for the current platform.
+ // url: String
+ // URL to request
+ // options: dojo/request.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ request.post = function(url, options){
+ // summary:
+ // Send an HTTP POST request using the default transport for the current platform.
+ // url: String
+ // URL to request
+ // options: dojo/request.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ request.put = function(url, options){
+ // summary:
+ // Send an HTTP POST request using the default transport for the current platform.
+ // url: String
+ // URL to request
+ // options: dojo/request.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ request.del = function(url, options){
+ // summary:
+ // Send an HTTP DELETE request using the default transport for the current platform.
+ // url: String
+ // URL to request
+ // options: dojo/request.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ =====*/
+ return request;
+});
diff --git a/src/main/resources/static/dojo/request/default.js b/src/main/resources/static/dojo/request/default.js
new file mode 100644
index 0000000000000000000000000000000000000000..b82aacf176c31a3ae400dc949305103caf4f8da8
--- /dev/null
+++ b/src/main/resources/static/dojo/request/default.js
@@ -0,0 +1,32 @@
+define([
+ 'exports',
+ 'require',
+ '../has'
+], function(exports, require, has){
+ var defId = has('config-requestProvider'),
+ platformId;
+
+ if(has('host-browser')){
+ platformId = './xhr';
+ }else if(has('host-node')){
+ platformId = './node';
+ /* TODO:
+ }else if(has('host-rhino')){
+ platformId = './rhino';
+ */
+ }
+
+ if(!defId){
+ defId = platformId;
+ }
+
+ exports.getPlatformDefaultId = function(){
+ return platformId;
+ };
+
+ exports.load = function(id, parentRequire, loaded, config){
+ require([id == 'platform' ? platformId : defId], function(provider){
+ loaded(provider);
+ });
+ };
+});
diff --git a/src/main/resources/static/dojo/request/handlers.js b/src/main/resources/static/dojo/request/handlers.js
new file mode 100644
index 0000000000000000000000000000000000000000..e04c2293f6ea61df3f4420093a302230761e87de
--- /dev/null
+++ b/src/main/resources/static/dojo/request/handlers.js
@@ -0,0 +1,74 @@
+define([
+ '../json',
+ '../_base/kernel',
+ '../_base/array',
+ '../has',
+ '../has!dom?../selector/_loader' // only included for has() qsa tests
+], function(JSON, kernel, array, has){
+ has.add('activex', typeof ActiveXObject !== 'undefined');
+ has.add('dom-parser', function(global){
+ return 'DOMParser' in global;
+ });
+
+ var handleXML;
+ if(has('activex')){
+ // GUIDs obtained from http://msdn.microsoft.com/en-us/library/ms757837(VS.85).aspx
+ var dp = [
+ 'Msxml2.DOMDocument.6.0',
+ 'Msxml2.DOMDocument.4.0',
+ 'MSXML2.DOMDocument.3.0',
+ 'MSXML.DOMDocument' // 2.0
+ ];
+
+ handleXML = function(response){
+ var result = response.data;
+
+ if(result && has('dom-qsa2.1') && !result.querySelectorAll && has('dom-parser')){
+ // http://bugs.dojotoolkit.org/ticket/15631
+ // IE9 supports a CSS3 querySelectorAll implementation, but the DOM implementation
+ // returned by IE9 xhr.responseXML does not. Manually create the XML DOM to gain
+ // the fuller-featured implementation and avoid bugs caused by the inconsistency
+ result = new DOMParser().parseFromString(response.text, 'application/xml');
+ }
+
+ if(!result || !result.documentElement){
+ var text = response.text;
+ array.some(dp, function(p){
+ try{
+ var dom = new ActiveXObject(p);
+ dom.async = false;
+ dom.loadXML(text);
+ result = dom;
+ }catch(e){ return false; }
+ return true;
+ });
+ }
+
+ return result;
+ };
+ }
+
+ var handlers = {
+ 'javascript': function(response){
+ return kernel.eval(response.text || '');
+ },
+ 'json': function(response){
+ return JSON.parse(response.text || null);
+ },
+ 'xml': handleXML
+ };
+
+ function handle(response){
+ var handler = handlers[response.options.handleAs];
+
+ response.data = handler ? handler(response) : (response.data || response.text);
+
+ return response;
+ }
+
+ handle.register = function(name, handler){
+ handlers[name] = handler;
+ };
+
+ return handle;
+});
diff --git a/src/main/resources/static/dojo/request/iframe.js b/src/main/resources/static/dojo/request/iframe.js
new file mode 100644
index 0000000000000000000000000000000000000000..a7576ac88f0ebd1a171a872a4a1e870a6141ae98
--- /dev/null
+++ b/src/main/resources/static/dojo/request/iframe.js
@@ -0,0 +1,431 @@
+define([
+ 'module',
+ 'require',
+ './watch',
+ './util',
+ './handlers',
+ '../_base/lang',
+ '../io-query',
+ '../query',
+ '../has',
+ '../dom',
+ '../dom-construct',
+ '../_base/window',
+ '../NodeList-dom'/*=====,
+ '../request',
+ '../_base/declare' =====*/
+], function(module, require, watch, util, handlers, lang, ioQuery, query, has, dom, domConstruct, win/*=====, NodeList, request, declare =====*/){
+ var mid = module.id.replace(/[\/\.\-]/g, '_'),
+ onload = mid + '_onload';
+
+ if(!win.global[onload]){
+ win.global[onload] = function(){
+ var dfd = iframe._currentDfd;
+ if(!dfd){
+ iframe._fireNextRequest();
+ return;
+ }
+
+ var response = dfd.response,
+ options = response.options,
+ formNode = dom.byId(options.form) || dfd._tmpForm;
+
+ if(formNode){
+ // remove all the hidden content inputs
+ var toClean = dfd._contentToClean;
+ for(var i=0; i',
+ win.body());
+
+ win.global[name] = frame;
+
+ return frame;
+ }
+
+ function setSrc(_iframe, src, replace){
+ var frame = win.global.frames[_iframe.name];
+
+ if(frame.contentWindow){
+ // We have an iframe node instead of the window
+ frame = frame.contentWindow;
+ }
+
+ try{
+ if(!replace){
+ frame.location = src;
+ }else{
+ frame.location.replace(src);
+ }
+ }catch(e){
+ console.log('dojo/request/iframe.setSrc: ', e);
+ }
+ }
+
+ function doc(iframeNode){
+ if(iframeNode.contentDocument){
+ return iframeNode.contentDocument;
+ }
+ var name = iframeNode.name;
+ if(name){
+ var iframes = win.doc.getElementsByTagName('iframe');
+ if(iframeNode.document && iframes[name].contentWindow && iframes[name].contentWindow.document){
+ return iframes[name].contentWindow.document;
+ }else if(win.doc.frames[name] && win.doc.frames[name].document){
+ return win.doc.frames[name].document;
+ }
+ }
+ return null;
+ }
+
+ function createForm(){
+ return domConstruct.create('form', {
+ name: mid + '_form',
+ style: {
+ position: 'absolute',
+ top: '-1000px',
+ left: '-1000px'
+ }
+ }, win.body());
+ }
+
+ function fireNextRequest(){
+ // summary:
+ // Internal method used to fire the next request in the queue.
+ var dfd;
+ try{
+ if(iframe._currentDfd || !iframe._dfdQueue.length){
+ return;
+ }
+ do{
+ dfd = iframe._currentDfd = iframe._dfdQueue.shift();
+ }while(dfd && (dfd.canceled || (dfd.isCanceled && dfd.isCanceled())) && iframe._dfdQueue.length);
+
+ if(!dfd || dfd.canceled || (dfd.isCanceled && dfd.isCanceled())){
+ iframe._currentDfd = null;
+ return;
+ }
+
+ var response = dfd.response,
+ options = response.options,
+ c2c = dfd._contentToClean = [],
+ formNode = dom.byId(options.form),
+ notify = util.notify,
+ data = options.data || null,
+ queryStr;
+
+ if(!dfd._legacy && options.method === 'POST' && !formNode){
+ formNode = dfd._tmpForm = createForm();
+ }else if(options.method === 'GET' && formNode && response.url.indexOf('?') > -1){
+ queryStr = response.url.slice(response.url.indexOf('?') + 1);
+ data = lang.mixin(ioQuery.queryToObject(queryStr), data);
+ }
+
+ if(formNode){
+ if(!dfd._legacy){
+ var parentNode = formNode;
+ do{
+ parentNode = parentNode.parentNode;
+ }while(parentNode !== win.doc.documentElement);
+
+ // Append the form node or some browsers won't work
+ if(!parentNode){
+ formNode.style.position = 'absolute';
+ formNode.style.left = '-1000px';
+ formNode.style.top = '-1000px';
+ win.body().appendChild(formNode);
+ }
+
+ if(!formNode.name){
+ formNode.name = mid + '_form';
+ }
+ }
+
+ // if we have things in data, we need to add them to the form
+ // before submission
+ if(data){
+ var createInput = function(name, value){
+ domConstruct.create('input', {
+ type: 'hidden',
+ name: name,
+ value: value
+ }, formNode);
+ c2c.push(name);
+ };
+ for(var x in data){
+ var val = data[x];
+ if(lang.isArray(val) && val.length > 1){
+ for(var i=0; i -1 ? '&' : '?') + extra;
+ notify && notify.emit('send', response, dfd.promise.cancel);
+ iframe._notifyStart(response);
+ iframe.setSrc(iframe._frame, tmpUrl, true);
+ }
+ }catch(e){
+ dfd.reject(e);
+ }
+ }
+
+ // dojo/request/watch handlers
+ function isValid(response){
+ return !this.isFulfilled();
+ }
+ function isReady(response){
+ return !!this._finished;
+ }
+ function handleResponse(response, error){
+ if(!error){
+ try{
+ var options = response.options,
+ doc = iframe.doc(iframe._frame),
+ handleAs = options.handleAs;
+
+ if(handleAs !== 'html'){
+ if(handleAs === 'xml'){
+ // IE6-8 have to parse the XML manually. See http://bugs.dojotoolkit.org/ticket/6334
+ if(doc.documentElement.tagName.toLowerCase() === 'html'){
+ query('a', doc.documentElement).orphan();
+ var xmlText = doc.documentElement.innerText;
+ xmlText = xmlText.replace(/>\s+<');
+ response.text = lang.trim(xmlText);
+ }else{
+ response.data = doc;
+ }
+ }else{
+ // 'json' and 'javascript' and 'text'
+ response.text = doc.getElementsByTagName('textarea')[0].value; // text
+ }
+ handlers(response);
+ }else{
+ response.data = doc;
+ }
+ }catch(e){
+ error = e;
+ }
+ }
+
+ if(error){
+ this.reject(error);
+ }else if(this._finished){
+ this.resolve(response);
+ }else{
+ this.reject(new Error('Invalid dojo/request/iframe request state'));
+ }
+ }
+ function last(response){
+ this._callNext();
+ }
+
+ var defaultOptions = {
+ method: 'POST'
+ };
+ function iframe(url, options, returnDeferred){
+ var response = util.parseArgs(url, util.deepCreate(defaultOptions, options), true);
+ url = response.url;
+ options = response.options;
+
+ if(options.method !== 'GET' && options.method !== 'POST'){
+ throw new Error(options.method + ' not supported by dojo/request/iframe');
+ }
+
+ if(!iframe._frame){
+ iframe._frame = iframe.create(iframe._iframeName, onload + '();');
+ }
+
+ var dfd = util.deferred(response, null, isValid, isReady, handleResponse, last);
+ dfd._callNext = function(){
+ if(!this._calledNext){
+ this._calledNext = true;
+ iframe._currentDfd = null;
+ iframe._fireNextRequest();
+ }
+ };
+ dfd._legacy = returnDeferred;
+
+ iframe._dfdQueue.push(dfd);
+ iframe._fireNextRequest();
+
+ watch(dfd);
+
+ return returnDeferred ? dfd : dfd.promise;
+ }
+
+ /*=====
+ iframe = function(url, options){
+ // summary:
+ // Sends a request using an iframe element with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/iframe.__Options?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ iframe.__BaseOptions = declare(request.__BaseOptions, {
+ // form: DOMNode?
+ // A form node to use to submit data to the server.
+ // data: String|Object?
+ // Data to transfer. When making a GET request, this will
+ // be converted to key=value parameters and appended to the
+ // URL.
+ });
+ iframe.__MethodOptions = declare(null, {
+ // method: String?
+ // The HTTP method to use to make the request. Must be
+ // uppercase. Only `"GET"` and `"POST"` are accepted.
+ // Default is `"POST"`.
+ });
+ iframe.__Options = declare([iframe.__BaseOptions, iframe.__MethodOptions]);
+
+ iframe.get = function(url, options){
+ // summary:
+ // Send an HTTP GET request using an iframe element with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/iframe.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ iframe.post = function(url, options){
+ // summary:
+ // Send an HTTP POST request using an iframe element with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/iframe.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ =====*/
+ iframe.create = create;
+ iframe.doc = doc;
+ iframe.setSrc = setSrc;
+
+ // TODO: Make these truly private in 2.0
+ iframe._iframeName = mid + '_IoIframe';
+ iframe._notifyStart = function(){};
+ iframe._dfdQueue = [];
+ iframe._currentDfd = null;
+ iframe._fireNextRequest = fireNextRequest;
+
+ util.addCommonMethods(iframe, ['GET', 'POST']);
+
+ return iframe;
+});
diff --git a/src/main/resources/static/dojo/request/node.js b/src/main/resources/static/dojo/request/node.js
new file mode 100644
index 0000000000000000000000000000000000000000..8b9d5b65a2dbe51117a905b301265b78feeb63f1
--- /dev/null
+++ b/src/main/resources/static/dojo/request/node.js
@@ -0,0 +1,195 @@
+define([
+ 'require',
+ './util',
+ './handlers',
+ '../errors/RequestTimeoutError',
+ '../node!http',
+ '../node!https',
+ '../node!url',
+ '../node!stream'/*=====,
+ '../request',
+ '../_base/declare' =====*/
+], function(require, util, handlers, RequestTimeoutError, http, https, URL, stream/*=====, request, declare =====*/){
+ var Stream = stream.Stream,
+ undefined;
+
+ var defaultOptions = {
+ method: 'GET',
+ query: null,
+ data: undefined,
+ headers: {}
+ };
+ function node(url, options){
+ var response = util.parseArgs(url, util.deepCreate(defaultOptions, options), options && options.data instanceof Stream);
+ url = response.url;
+ options = response.options;
+
+ var def = util.deferred(
+ response,
+ function(dfd, response){
+ response.clientRequest.abort();
+ }
+ );
+
+ url = URL.parse(url);
+
+ var reqOptions = response.requestOptions = {
+ hostname: url.hostname,
+ port: url.port,
+ socketPath: options.socketPath,
+ method: options.method,
+ headers: options.headers,
+ agent: options.agent,
+ pfx: options.pfx,
+ key: options.key,
+ passphrase: options.passphrase,
+ cert: options.cert,
+ ca: options.ca,
+ ciphers: options.ciphers,
+ rejectUnauthorized: options.rejectUnauthorized === false ? false : true
+ };
+ if(url.path){
+ reqOptions.path = url.path;
+ }
+ if(options.user || options.password){
+ reqOptions.auth = (options.user||'') + ':' + (options.password||'');
+ }
+ var req = response.clientRequest = (url.protocol === 'https:' ? https : http).request(reqOptions);
+
+ if(options.socketOptions){
+ if('timeout' in options.socketOptions){
+ req.setTimeout(options.socketOptions.timeout);
+ }
+ if('noDelay' in options.socketOptions){
+ req.setNoDelay(options.socketOptions.noDelay);
+ }
+ if('keepAlive' in options.socketOptions){
+ var initialDelay = options.socketOptions.keepAlive;
+ req.setKeepAlive(initialDelay >= 0, initialDelay || 0);
+ }
+ }
+
+ req.on('socket', function(){
+ response.hasSocket = true;
+ def.progress(response);
+ });
+
+ req.on('response', function(clientResponse){
+ response.clientResponse = clientResponse;
+ response.status = clientResponse.statusCode;
+ response.getHeader = function(headerName){
+ return clientResponse.headers[headerName.toLowerCase()] || null;
+ };
+
+ var body = [];
+ clientResponse.on('data', function(chunk){
+ body.push(chunk);
+
+ // TODO: progress updates via the deferred
+ });
+ clientResponse.on('end', function(){
+ if(timeout){
+ clearTimeout(timeout);
+ }
+ response.text = body.join('');
+ try{
+ handlers(response);
+ def.resolve(response);
+ }catch(error){
+ def.reject(error);
+ }
+ });
+ });
+
+ req.on('error', def.reject);
+
+ if(options.data){
+ if(typeof options.data === 'string'){
+ req.end(options.data);
+ }else{
+ options.data.pipe(req);
+ }
+ }else{
+ req.end();
+ }
+
+ if(options.timeout){
+ var timeout = setTimeout(function(){
+ def.cancel(new RequestTimeoutError(response));
+ }, options.timeout);
+ }
+
+ return def.promise;
+ }
+
+ /*=====
+ node = function(url, options){
+ // summary:
+ // Sends a request using the included http or https interface from node.js
+ // with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/node.__Options?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ node.__BaseOptions = declare(request.__BaseOptions, {
+ // data: String|Object|Stream?
+ // Data to transfer. This is ignored for GET and DELETE
+ // requests.
+ // headers: Object?
+ // Headers to use for the request.
+ // user: String?
+ // Username to use during the request.
+ // password: String?
+ // Password to use during the request.
+ });
+ node.__MethodOptions = declare(null, {
+ // method: String?
+ // The HTTP method to use to make the request. Must be
+ // uppercase. Default is `"GET"`.
+ });
+ node.__Options = declare([node.__BaseOptions, node.__MethodOptions]);
+
+ node.get = function(url, options){
+ // summary:
+ // Send an HTTP GET request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/node.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ node.post = function(url, options){
+ // summary:
+ // Send an HTTP POST request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/node.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ node.put = function(url, options){
+ // summary:
+ // Send an HTTP PUT request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/node.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ node.del = function(url, options){
+ // summary:
+ // Send an HTTP DELETE request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/node.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ =====*/
+
+ util.addCommonMethods(node);
+
+ return node;
+});
diff --git a/src/main/resources/static/dojo/request/notify.js b/src/main/resources/static/dojo/request/notify.js
new file mode 100644
index 0000000000000000000000000000000000000000..8392cb583c5cabd4847b5ceca492e7ab8d4f734c
--- /dev/null
+++ b/src/main/resources/static/dojo/request/notify.js
@@ -0,0 +1,74 @@
+define(['../Evented', '../_base/lang', './util'], function(Evented, lang, util){
+ // module:
+ // dojo/request/notify
+ // summary:
+ // Global notification API for dojo/request. Notifications will
+ // only be emitted if this module is required.
+ //
+ // | require('dojo/request', 'dojo/request/notify',
+ // | function(request, notify){
+ // | notify('load', function(response){
+ // | if(response.url === 'someUrl.html'){
+ // | console.log('Loaded!');
+ // | }
+ // | });
+ // | request.get('someUrl.html');
+ // | }
+ // | );
+
+ var pubCount = 0,
+ slice = [].slice;
+
+ var hub = lang.mixin(new Evented, {
+ onsend: function(data){
+ if(!pubCount){
+ this.emit('start');
+ }
+ pubCount++;
+ },
+ _onload: function(data){
+ this.emit('done', data);
+ },
+ _onerror: function(data){
+ this.emit('done', data);
+ },
+ _ondone: function(data){
+ if(--pubCount <= 0){
+ pubCount = 0;
+ this.emit('stop');
+ }
+ },
+ emit: function(type, event){
+ var result = Evented.prototype.emit.apply(this, arguments);
+
+ // After all event handlers have run, run _on* handler
+ if(this['_on' + type]){
+ this['_on' + type].apply(this, slice.call(arguments, 1));
+ }
+ return result;
+ }
+ });
+
+ function notify(type, listener){
+ // summary:
+ // Register a listener to be notified when an event
+ // in dojo/request happens.
+ // type: String?
+ // The event to listen for. Events emitted: "start", "send",
+ // "load", "error", "done", "stop".
+ // listener: Function?
+ // A callback to be run when an event happens.
+ // returns:
+ // A signal object that can be used to cancel the listener.
+ // If remove() is called on this signal object, it will
+ // stop the listener from being executed.
+ return hub.on(type, listener);
+ }
+ notify.emit = function(type, event, cancel){
+ return hub.emit(type, event, cancel);
+ };
+
+ // Attach notify to dojo/request/util to avoid
+ // try{ require('./notify'); }catch(e){}
+ return util.notify = notify;
+});
diff --git a/src/main/resources/static/dojo/request/registry.js b/src/main/resources/static/dojo/request/registry.js
new file mode 100644
index 0000000000000000000000000000000000000000..bda7e5d44ea87b368fd1c5e62107b47a8d8ad87e
--- /dev/null
+++ b/src/main/resources/static/dojo/request/registry.js
@@ -0,0 +1,85 @@
+define([
+ 'require',
+ '../_base/array',
+ './default!platform',
+ './util'
+], function(require, array, fallbackProvider, util){
+ var providers = [];
+
+ function request(url, options){
+ var matchers = providers.slice(0),
+ i = 0,
+ matcher;
+
+ while(matcher=matchers[i++]){
+ if(matcher(url, options)){
+ return matcher.request.call(null, url, options);
+ }
+ }
+
+ return fallbackProvider.apply(null, arguments);
+ }
+
+ function createMatcher(match, provider){
+ var matcher;
+
+ if(provider){
+ if(match.test){
+ // RegExp
+ matcher = function(url){
+ return match.test(url);
+ };
+ }else if(match.apply && match.call){
+ matcher = function(){
+ return match.apply(null, arguments);
+ };
+ }else{
+ matcher = function(url){
+ return url === match;
+ };
+ }
+
+ matcher.request = provider;
+ }else{
+ // If only one argument was passed, assume it is a provider function
+ // to apply unconditionally to all URLs
+ matcher = function(){
+ return true;
+ };
+
+ matcher.request = match;
+ }
+
+ return matcher;
+ }
+
+ request.register = function(url, provider, first){
+ var matcher = createMatcher(url, provider);
+ providers[(first ? 'unshift' : 'push')](matcher);
+
+ return {
+ remove: function(){
+ var idx;
+ if(~(idx = array.indexOf(providers, matcher))){
+ providers.splice(idx, 1);
+ }
+ }
+ };
+ };
+
+ request.load = function(id, parentRequire, loaded, config){
+ if(id){
+ // if there's an id, load and set the fallback provider
+ require([id], function(fallback){
+ fallbackProvider = fallback;
+ loaded(request);
+ });
+ }else{
+ loaded(request);
+ }
+ };
+
+ util.addCommonMethods(request);
+
+ return request;
+});
diff --git a/src/main/resources/static/dojo/request/script.js b/src/main/resources/static/dojo/request/script.js
new file mode 100644
index 0000000000000000000000000000000000000000..b0ee1486e198bb53b034f15da9b7905497fc6351
--- /dev/null
+++ b/src/main/resources/static/dojo/request/script.js
@@ -0,0 +1,219 @@
+define([
+ 'module',
+ './watch',
+ './util',
+ '../_base/array',
+ '../_base/lang',
+ '../on',
+ '../dom',
+ '../dom-construct',
+ '../has',
+ '../_base/window'/*=====,
+ '../request',
+ '../_base/declare' =====*/
+], function(module, watch, util, array, lang, on, dom, domConstruct, has, win/*=====, request, declare =====*/){
+ has.add('script-readystatechange', function(global, document){
+ var script = document.createElement('script');
+ return typeof script['onreadystatechange'] !== 'undefined' &&
+ (typeof global['opera'] === 'undefined' || global['opera'].toString() !== '[object Opera]');
+ });
+
+ var mid = module.id.replace(/[\/\.\-]/g, '_'),
+ counter = 0,
+ loadEvent = has('script-readystatechange') ? 'readystatechange' : 'load',
+ readyRegExp = /complete|loaded/,
+ callbacks = this[mid + '_callbacks'] = {},
+ deadScripts = [];
+
+ function attach(id, url, frameDoc){
+ var doc = (frameDoc || win.doc),
+ element = doc.createElement('script');
+
+ element.type = 'text/javascript';
+ element.src = url;
+ element.id = id;
+ element.async = true;
+ element.charset = 'utf-8';
+
+ return doc.getElementsByTagName('head')[0].appendChild(element);
+ }
+
+ function remove(id, frameDoc, cleanup){
+ domConstruct.destroy(dom.byId(id, frameDoc));
+
+ if(callbacks[id]){
+ if(cleanup){
+ // set callback to a function that deletes itself so requests that
+ // are in-flight don't error out when returning and also
+ // clean up after themselves
+ callbacks[id] = function(){
+ delete callbacks[id];
+ };
+ }else{
+ delete callbacks[id];
+ }
+ }
+ }
+
+ function _addDeadScript(dfd){
+ // Be sure to check ioArgs because it can dynamically change in the dojox/io plugins.
+ // See http://bugs.dojotoolkit.org/ticket/15890.
+ var options = dfd.response.options,
+ frameDoc = options.ioArgs ? options.ioArgs.frameDoc : options.frameDoc;
+
+ deadScripts.push({ id: dfd.id, frameDoc: frameDoc });
+
+ if(options.ioArgs){
+ options.ioArgs.frameDoc = null;
+ }
+ options.frameDoc = null;
+ }
+
+ function canceler(dfd, response){
+ if(dfd.canDelete){
+ //For timeouts and cancels, remove the script element immediately to
+ //avoid a response from it coming back later and causing trouble.
+ script._remove(dfd.id, response.options.frameDoc, true);
+ }
+ }
+ function isValid(response){
+ //Do script cleanup here. We wait for one inflight pass
+ //to make sure we don't get any weird things by trying to remove a script
+ //tag that is part of the call chain (IE 6 has been known to
+ //crash in that case).
+ if(deadScripts && deadScripts.length){
+ array.forEach(deadScripts, function(_script){
+ script._remove(_script.id, _script.frameDoc);
+ _script.frameDoc = null;
+ });
+ deadScripts = [];
+ }
+
+ return response.options.jsonp ? !response.data : true;
+ }
+ function isReadyScript(response){
+ return !!this.scriptLoaded;
+ }
+ function isReadyCheckString(response){
+ var checkString = response.options.checkString;
+
+ return checkString && eval('typeof(' + checkString + ') !== "undefined"');
+ }
+ function handleResponse(response, error){
+ if(this.canDelete){
+ _addDeadScript(this);
+ }
+ if(error){
+ this.reject(error);
+ }else{
+ this.resolve(response);
+ }
+ }
+
+ function script(url, options, returnDeferred){
+ var response = util.parseArgs(url, util.deepCopy({}, options));
+ url = response.url;
+ options = response.options;
+
+ var dfd = util.deferred(
+ response,
+ canceler,
+ isValid,
+ options.jsonp ? null : (options.checkString ? isReadyCheckString : isReadyScript),
+ handleResponse
+ );
+
+ lang.mixin(dfd, {
+ id: mid + (counter++),
+ canDelete: false
+ });
+
+ if(options.jsonp){
+ var queryParameter = new RegExp('[?&]' + options.jsonp + '=');
+ if(!queryParameter.test(url)){
+ url += (~url.indexOf('?') ? '&' : '?') +
+ options.jsonp + '=' +
+ (options.frameDoc ? 'parent.' : '') +
+ mid + '_callbacks.' + dfd.id;
+ }
+
+ dfd.canDelete = true;
+ callbacks[dfd.id] = function(json){
+ response.data = json;
+ dfd.handleResponse(response);
+ };
+ }
+
+ if(util.notify){
+ util.notify.emit('send', response, dfd.promise.cancel);
+ }
+
+ if(!options.canAttach || options.canAttach(dfd)){
+ var node = script._attach(dfd.id, url, options.frameDoc);
+
+ if(!options.jsonp && !options.checkString){
+ var handle = on(node, loadEvent, function(evt){
+ if(evt.type === 'load' || readyRegExp.test(node.readyState)){
+ handle.remove();
+ dfd.scriptLoaded = evt;
+ }
+ });
+ }
+ }
+
+ watch(dfd);
+
+ return returnDeferred ? dfd : dfd.promise;
+ }
+ script.get = script;
+ /*=====
+ script = function(url, options){
+ // summary:
+ // Sends a request using a script element with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/script.__Options?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ script.__BaseOptions = declare(request.__BaseOptions, {
+ // jsonp: String?
+ // The URL parameter name that indicates the JSONP callback string.
+ // For instance, when using Yahoo JSONP calls it is normally,
+ // jsonp: "callback". For AOL JSONP calls it is normally
+ // jsonp: "c".
+ // checkString: String?
+ // A string of JavaScript that when evaluated like so:
+ // "typeof(" + checkString + ") != 'undefined'"
+ // being true means that the script fetched has been loaded.
+ // Do not use this if doing a JSONP type of call (use `jsonp` instead).
+ // frameDoc: Document?
+ // The Document object of a child iframe. If this is passed in, the script
+ // will be attached to that document. This can be helpful in some comet long-polling
+ // scenarios with Firefox and Opera.
+ });
+ script.__MethodOptions = declare(null, {
+ // method: String?
+ // This option is ignored. All requests using this transport are
+ // GET requests.
+ });
+ script.__Options = declare([script.__BaseOptions, script.__MethodOptions]);
+
+ script.get = function(url, options){
+ // summary:
+ // Send an HTTP GET request using a script element with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/script.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ =====*/
+
+ // TODO: Remove in 2.0
+ script._attach = attach;
+ script._remove = remove;
+ script._callbacksProperty = mid + '_callbacks';
+
+ return script;
+});
diff --git a/src/main/resources/static/dojo/request/util.js b/src/main/resources/static/dojo/request/util.js
new file mode 100644
index 0000000000000000000000000000000000000000..86a9b42d1dd7c8f2bb25661dcc90fcfaa510e124
--- /dev/null
+++ b/src/main/resources/static/dojo/request/util.js
@@ -0,0 +1,158 @@
+define([
+ 'exports',
+ '../errors/RequestError',
+ '../errors/CancelError',
+ '../Deferred',
+ '../io-query',
+ '../_base/array',
+ '../_base/lang',
+ '../promise/Promise'
+], function(exports, RequestError, CancelError, Deferred, ioQuery, array, lang, Promise){
+ exports.deepCopy = function deepCopy(target, source){
+ for(var name in source){
+ var tval = target[name],
+ sval = source[name];
+ if(tval !== sval){
+ if(tval && typeof tval === 'object' && sval && typeof sval === 'object'){
+ exports.deepCopy(tval, sval);
+ }else{
+ target[name] = sval;
+ }
+ }
+ }
+ return target;
+ };
+
+ exports.deepCreate = function deepCreate(source, properties){
+ properties = properties || {};
+ var target = lang.delegate(source),
+ name, value;
+
+ for(name in source){
+ value = source[name];
+
+ if(value && typeof value === 'object'){
+ target[name] = exports.deepCreate(value, properties[name]);
+ }
+ }
+ return exports.deepCopy(target, properties);
+ };
+
+ var freeze = Object.freeze || function(obj){ return obj; };
+ function okHandler(response){
+ return freeze(response);
+ }
+ function dataHandler (response) {
+ return response.data || response.text;
+ }
+
+ exports.deferred = function deferred(response, cancel, isValid, isReady, handleResponse, last){
+ var def = new Deferred(function(reason){
+ cancel && cancel(def, response);
+
+ if(!reason || !(reason instanceof RequestError) && !(reason instanceof CancelError)){
+ return new CancelError('Request canceled', response);
+ }
+ return reason;
+ });
+
+ def.response = response;
+ def.isValid = isValid;
+ def.isReady = isReady;
+ def.handleResponse = handleResponse;
+
+ function errHandler(error){
+ error.response = response;
+ throw error;
+ }
+ var responsePromise = def.then(okHandler).otherwise(errHandler);
+
+ if(exports.notify){
+ responsePromise.then(
+ lang.hitch(exports.notify, 'emit', 'load'),
+ lang.hitch(exports.notify, 'emit', 'error')
+ );
+ }
+
+ var dataPromise = responsePromise.then(dataHandler);
+
+ // http://bugs.dojotoolkit.org/ticket/16794
+ // The following works around a leak in IE9 through the
+ // prototype using lang.delegate on dataPromise and
+ // assigning the result a property with a reference to
+ // responsePromise.
+ var promise = new Promise();
+ for (var prop in dataPromise) {
+ if (dataPromise.hasOwnProperty(prop)) {
+ promise[prop] = dataPromise[prop];
+ }
+ }
+ promise.response = responsePromise;
+ freeze(promise);
+ // End leak fix
+
+
+ if(last){
+ def.then(function(response){
+ last.call(def, response);
+ }, function(error){
+ last.call(def, response, error);
+ });
+ }
+
+ def.promise = promise;
+ def.then = promise.then;
+
+ return def;
+ };
+
+ exports.addCommonMethods = function addCommonMethods(provider, methods){
+ array.forEach(methods||['GET', 'POST', 'PUT', 'DELETE'], function(method){
+ provider[(method === 'DELETE' ? 'DEL' : method).toLowerCase()] = function(url, options){
+ options = lang.delegate(options||{});
+ options.method = method;
+ return provider(url, options);
+ };
+ });
+ };
+
+ exports.parseArgs = function parseArgs(url, options, skipData){
+ var data = options.data,
+ query = options.query;
+
+ if(data && !skipData){
+ if(typeof data === 'object'){
+ options.data = ioQuery.objectToQuery(data);
+ }
+ }
+
+ if(query){
+ if(typeof query === 'object'){
+ query = ioQuery.objectToQuery(query);
+ }
+ if(options.preventCache){
+ query += (query ? '&' : '') + 'request.preventCache=' + (+(new Date));
+ }
+ }else if(options.preventCache){
+ query = 'request.preventCache=' + (+(new Date));
+ }
+
+ if(url && query){
+ url += (~url.indexOf('?') ? '&' : '?') + query;
+ }
+
+ return {
+ url: url,
+ options: options,
+ getHeader: function(headerName){ return null; }
+ };
+ };
+
+ exports.checkStatus = function(stat){
+ stat = stat || 0;
+ return (stat >= 200 && stat < 300) || // allow any 2XX response code
+ stat === 304 || // or, get it out of the cache
+ stat === 1223 || // or, Internet Explorer mangled the status code
+ !stat; // or, we're Titanium/browser chrome/chrome extension requesting a local file
+ };
+});
diff --git a/src/main/resources/static/dojo/request/watch.js b/src/main/resources/static/dojo/request/watch.js
new file mode 100644
index 0000000000000000000000000000000000000000..6e0f2f4f1d54d5e6fb07d439c0ba7ead219927e2
--- /dev/null
+++ b/src/main/resources/static/dojo/request/watch.js
@@ -0,0 +1,109 @@
+define([
+ './util',
+ '../errors/RequestTimeoutError',
+ '../errors/CancelError',
+ '../_base/array',
+ '../has!host-browser?../_base/window:',
+ '../has!host-browser?dom-addeventlistener?:../on:'
+], function(util, RequestTimeoutError, CancelError, array, win, on){
+ // avoid setting a timer per request. It degrades performance on IE
+ // something fierece if we don't use unified loops.
+ var _inFlightIntvl = null,
+ _inFlight = [];
+
+ function watchInFlight(){
+ // summary:
+ // internal method that checks each inflight XMLHttpRequest to see
+ // if it has completed or if the timeout situation applies.
+
+ var now = +(new Date);
+
+ // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
+ for(var i = 0, dfd; i < _inFlight.length && (dfd = _inFlight[i]); i++){
+ var response = dfd.response,
+ options = response.options;
+ if((dfd.isCanceled && dfd.isCanceled()) || (dfd.isValid && !dfd.isValid(response))){
+ _inFlight.splice(i--, 1);
+ watch._onAction && watch._onAction();
+ }else if(dfd.isReady && dfd.isReady(response)){
+ _inFlight.splice(i--, 1);
+ dfd.handleResponse(response);
+ watch._onAction && watch._onAction();
+ }else if(dfd.startTime){
+ // did we timeout?
+ if(dfd.startTime + (options.timeout || 0) < now){
+ _inFlight.splice(i--, 1);
+ // Cancel the request so the io module can do appropriate cleanup.
+ dfd.cancel(new RequestTimeoutError('Timeout exceeded', response));
+ watch._onAction && watch._onAction();
+ }
+ }
+ }
+
+ watch._onInFlight && watch._onInFlight(dfd);
+
+ if(!_inFlight.length){
+ clearInterval(_inFlightIntvl);
+ _inFlightIntvl = null;
+ }
+ }
+
+ function watch(dfd){
+ // summary:
+ // Watches the io request represented by dfd to see if it completes.
+ // dfd: Deferred
+ // The Deferred object to watch.
+ // response: Object
+ // The object used as the value of the request promise.
+ // validCheck: Function
+ // Function used to check if the IO request is still valid. Gets the dfd
+ // object as its only argument.
+ // ioCheck: Function
+ // Function used to check if basic IO call worked. Gets the dfd
+ // object as its only argument.
+ // resHandle: Function
+ // Function used to process response. Gets the dfd
+ // object as its only argument.
+ if(dfd.response.options.timeout){
+ dfd.startTime = +(new Date);
+ }
+
+ if(dfd.isFulfilled()){
+ // bail out if the deferred is already fulfilled
+ return;
+ }
+
+ _inFlight.push(dfd);
+ if(!_inFlightIntvl){
+ _inFlightIntvl = setInterval(watchInFlight, 50);
+ }
+
+ // handle sync requests separately from async:
+ // http://bugs.dojotoolkit.org/ticket/8467
+ if(dfd.response.options.sync){
+ watchInFlight();
+ }
+ }
+
+ watch.cancelAll = function cancelAll(){
+ // summary:
+ // Cancels all pending IO requests, regardless of IO type
+ try{
+ array.forEach(_inFlight, function(dfd){
+ try{
+ dfd.cancel(new CancelError('All requests canceled.'));
+ }catch(e){}
+ });
+ }catch(e){}
+ };
+
+ if(win && on && win.doc.attachEvent){
+ // Automatically call cancel all io calls on unload in IE
+ // http://bugs.dojotoolkit.org/ticket/2357
+ on(win.global, 'unload', function(){
+ watch.cancelAll();
+ });
+ }
+
+ return watch;
+});
diff --git a/src/main/resources/static/dojo/request/xhr.js b/src/main/resources/static/dojo/request/xhr.js
new file mode 100644
index 0000000000000000000000000000000000000000..647c2ec0b476e1c7ecb0c2d0089aa1e77c635046
--- /dev/null
+++ b/src/main/resources/static/dojo/request/xhr.js
@@ -0,0 +1,316 @@
+define([
+ '../errors/RequestError',
+ './watch',
+ './handlers',
+ './util',
+ '../has'/*=====,
+ '../request',
+ '../_base/declare' =====*/
+], function(RequestError, watch, handlers, util, has/*=====, request, declare =====*/){
+ has.add('native-xhr', function(){
+ // if true, the environment has a native XHR implementation
+ return typeof XMLHttpRequest !== 'undefined';
+ });
+ has.add('dojo-force-activex-xhr', function(){
+ return has('activex') && !document.addEventListener && window.location.protocol === 'file:';
+ });
+
+ has.add('native-xhr2', function(){
+ if(!has('native-xhr')){ return; }
+ var x = new XMLHttpRequest();
+ return typeof x['addEventListener'] !== 'undefined' &&
+ (typeof opera === 'undefined' || typeof x['upload'] !== 'undefined');
+ });
+
+ has.add('native-formdata', function(){
+ // if true, the environment has a native FormData implementation
+ return typeof FormData === 'function';
+ });
+
+ function handleResponse(response, error){
+ var _xhr = response.xhr;
+ response.status = response.xhr.status;
+ response.text = _xhr.responseText;
+
+ if(response.options.handleAs === 'xml'){
+ response.data = _xhr.responseXML;
+ }
+
+ if(!error){
+ try{
+ handlers(response);
+ }catch(e){
+ error = e;
+ }
+ }
+
+ if(error){
+ this.reject(error);
+ }else if(util.checkStatus(_xhr.status)){
+ this.resolve(response);
+ }else{
+ error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response);
+
+ this.reject(error);
+ }
+ }
+
+ var isValid, isReady, addListeners, cancel;
+ if(has('native-xhr2')){
+ // Any platform with XHR2 will only use the watch mechanism for timeout.
+
+ isValid = function(response){
+ // summary:
+ // Check to see if the request should be taken out of the watch queue
+ return !this.isFulfilled();
+ };
+ cancel = function(dfd, response){
+ // summary:
+ // Canceler for deferred
+ response.xhr.abort();
+ };
+ addListeners = function(_xhr, dfd, response){
+ // summary:
+ // Adds event listeners to the XMLHttpRequest object
+ function onLoad(evt){
+ dfd.handleResponse(response);
+ }
+ function onError(evt){
+ var _xhr = evt.target;
+ var error = new RequestError('Unable to load ' + response.url + ' status: ' + _xhr.status, response);
+ dfd.handleResponse(response, error);
+ }
+
+ function onProgress(evt){
+ if(evt.lengthComputable){
+ response.loaded = evt.loaded;
+ response.total = evt.total;
+ dfd.progress(response);
+ }
+ }
+
+ _xhr.addEventListener('load', onLoad, false);
+ _xhr.addEventListener('error', onError, false);
+ _xhr.addEventListener('progress', onProgress, false);
+
+ return function(){
+ _xhr.removeEventListener('load', onLoad, false);
+ _xhr.removeEventListener('error', onError, false);
+ _xhr.removeEventListener('progress', onProgress, false);
+ _xhr = null;
+ };
+ };
+ }else{
+ isValid = function(response){
+ return response.xhr.readyState; //boolean
+ };
+ isReady = function(response){
+ return 4 === response.xhr.readyState; //boolean
+ };
+ cancel = function(dfd, response){
+ // summary:
+ // canceller function for util.deferred call.
+ var xhr = response.xhr;
+ var _at = typeof xhr.abort;
+ if(_at === 'function' || _at === 'object' || _at === 'unknown'){
+ xhr.abort();
+ }
+ };
+ }
+
+ function getHeader(headerName){
+ return this.xhr.getResponseHeader(headerName);
+ }
+
+ var undefined,
+ defaultOptions = {
+ data: null,
+ query: null,
+ sync: false,
+ method: 'GET'
+ };
+ function xhr(url, options, returnDeferred){
+ var response = util.parseArgs(
+ url,
+ util.deepCreate(defaultOptions, options),
+ has('native-formdata') && options && options.data && options.data instanceof FormData
+ );
+ url = response.url;
+ options = response.options;
+
+ var remover,
+ last = function(){
+ remover && remover();
+ };
+
+ //Make the Deferred object for this xhr request.
+ var dfd = util.deferred(
+ response,
+ cancel,
+ isValid,
+ isReady,
+ handleResponse,
+ last
+ );
+ var _xhr = response.xhr = xhr._create();
+
+ if(!_xhr){
+ // If XHR factory somehow returns nothings,
+ // cancel the deferred.
+ dfd.cancel(new RequestError('XHR was not created'));
+ return returnDeferred ? dfd : dfd.promise;
+ }
+
+ response.getHeader = getHeader;
+
+ if(addListeners){
+ remover = addListeners(_xhr, dfd, response);
+ }
+
+ var data = options.data,
+ async = !options.sync,
+ method = options.method;
+
+ try{
+ // IE6 won't let you call apply() on the native function.
+ _xhr.open(method, url, async, options.user || undefined, options.password || undefined);
+
+ if(options.withCredentials){
+ _xhr.withCredentials = options.withCredentials;
+ }
+
+ var headers = options.headers,
+ contentType = 'application/x-www-form-urlencoded';
+ if(headers){
+ for(var hdr in headers){
+ if(hdr.toLowerCase() === 'content-type'){
+ contentType = headers[hdr];
+ }else if(headers[hdr]){
+ //Only add header if it has a value. This allows for instance, skipping
+ //insertion of X-Requested-With by specifying empty value.
+ _xhr.setRequestHeader(hdr, headers[hdr]);
+ }
+ }
+ }
+
+ if(contentType && contentType !== false){
+ _xhr.setRequestHeader('Content-Type', contentType);
+ }
+ if(!headers || !('X-Requested-With' in headers)){
+ _xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ }
+
+ if(util.notify){
+ util.notify.emit('send', response, dfd.promise.cancel);
+ }
+ _xhr.send(data);
+ }catch(e){
+ dfd.reject(e);
+ }
+
+ watch(dfd);
+ _xhr = null;
+
+ return returnDeferred ? dfd : dfd.promise;
+ }
+
+ /*=====
+ xhr = function(url, options){
+ // summary:
+ // Sends a request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/xhr.__Options?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ xhr.__BaseOptions = declare(request.__BaseOptions, {
+ // sync: Boolean?
+ // Whether to make a synchronous request or not. Default
+ // is `false` (asynchronous).
+ // data: String|Object|FormData?
+ // Data to transfer. This is ignored for GET and DELETE
+ // requests.
+ // headers: Object?
+ // Headers to use for the request.
+ // user: String?
+ // Username to use during the request.
+ // password: String?
+ // Password to use during the request.
+ // withCredentials: Boolean?
+ // For cross-site requests, whether to send credentials
+ // or not.
+ });
+ xhr.__MethodOptions = declare(null, {
+ // method: String?
+ // The HTTP method to use to make the request. Must be
+ // uppercase. Default is `"GET"`.
+ });
+ xhr.__Options = declare([xhr.__BaseOptions, xhr.__MethodOptions]);
+
+ xhr.get = function(url, options){
+ // summary:
+ // Send an HTTP GET request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/xhr.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ xhr.post = function(url, options){
+ // summary:
+ // Send an HTTP POST request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/xhr.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ xhr.put = function(url, options){
+ // summary:
+ // Send an HTTP PUT request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/xhr.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ xhr.del = function(url, options){
+ // summary:
+ // Send an HTTP DELETE request using XMLHttpRequest with the given URL and options.
+ // url: String
+ // URL to request
+ // options: dojo/request/xhr.__BaseOptions?
+ // Options for the request.
+ // returns: dojo/request.__Promise
+ };
+ =====*/
+ xhr._create = function(){
+ // summary:
+ // does the work of portably generating a new XMLHTTPRequest object.
+ throw new Error('XMLHTTP not available');
+ };
+ if(has('native-xhr') && !has('dojo-force-activex-xhr')){
+ xhr._create = function(){
+ return new XMLHttpRequest();
+ };
+ }else if(has('activex')){
+ try{
+ new ActiveXObject('Msxml2.XMLHTTP');
+ xhr._create = function(){
+ return new ActiveXObject('Msxml2.XMLHTTP');
+ };
+ }catch(e){
+ try{
+ new ActiveXObject('Microsoft.XMLHTTP');
+ xhr._create = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+ }catch(e){}
+ }
+ }
+
+ util.addCommonMethods(xhr);
+
+ return xhr;
+});
diff --git a/src/main/resources/static/dojo/require.js b/src/main/resources/static/dojo/require.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f02bca9abe1e953663fb3e50562a52c477ad3e0
--- /dev/null
+++ b/src/main/resources/static/dojo/require.js
@@ -0,0 +1,7 @@
+define(["./_base/loader"], function(loader){
+ return {
+ dynamic:0,
+ normalize:function(id){return id;},
+ load:loader.require
+ };
+});
diff --git a/src/main/resources/static/dojo/resources/LICENSE b/src/main/resources/static/dojo/resources/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..eb28b7e4df47e8434648a30d135bb7219ba5225e
--- /dev/null
+++ b/src/main/resources/static/dojo/resources/LICENSE
@@ -0,0 +1,30 @@
+License Disclaimer:
+
+All contents of this directory are Copyright (c) the Dojo Foundation, with the
+following exceptions:
+-------------------------------------------------------------------------------
+
+dojo.css:
+ * parts Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ Distributed under the terms of the BSD License
+
+The Program includes all or portions of the following software which was obtained under the terms and conditions of the BSD License.
+
+http://developer.yahoo.com/yui/license.html
+
+Copyright (c) 2007, Yahoo! Inc.
+ All rights reserved.
+ Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ * Neither the name of Yahoo! Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without
+specific prior written permission of Yahoo! Inc.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/static/dojo/resources/blank.gif b/src/main/resources/static/dojo/resources/blank.gif
new file mode 100644
index 0000000000000000000000000000000000000000..e565824aafafe632011b281cba976baf8b3ba89a
Binary files /dev/null and b/src/main/resources/static/dojo/resources/blank.gif differ
diff --git a/src/main/resources/static/dojo/resources/blank.html b/src/main/resources/static/dojo/resources/blank.html
new file mode 100644
index 0000000000000000000000000000000000000000..40fe7705a6f6210b60130dfeef4c2ff572c15d2c
--- /dev/null
+++ b/src/main/resources/static/dojo/resources/blank.html
@@ -0,0 +1 @@
+
diff --git a/src/main/resources/static/dojo/resources/dnd.css b/src/main/resources/static/dojo/resources/dnd.css
new file mode 100644
index 0000000000000000000000000000000000000000..5c8f1e6ad508e1ea5d4a887ec3918e2f3909876a
--- /dev/null
+++ b/src/main/resources/static/dojo/resources/dnd.css
@@ -0,0 +1,16 @@
+/* DnD avatar-specific settings */
+.dojoDndAvatar {font-size: 75%; color: black;}
+.dojoDndAvatarHeader td {padding-left: 20px; padding-right: 4px; height: 16px;}
+.dojoDndAvatarHeader {background: #ccc;}
+.dojoDndAvatarItem {background: #eee;}
+.dojoDndMove .dojoDndAvatarHeader {background-image: url(images/dndNoMove.png); background-repeat: no-repeat;}
+.dojoDndCopy .dojoDndAvatarHeader {background-image: url(images/dndNoCopy.png); background-repeat: no-repeat;}
+.dojoDndMove .dojoDndAvatarCanDrop .dojoDndAvatarHeader {background-image: url(images/dndMove.png); background-repeat: no-repeat;}
+.dojoDndCopy .dojoDndAvatarCanDrop .dojoDndAvatarHeader {background-image: url(images/dndCopy.png); background-repeat: no-repeat;}
+
+.dojoDndHandle {cursor: move;}
+.dojoDndIgnore {cursor: default;}
+
+.dj_a11y .dojoDndAvatar { font-size: 1em; font-weight:bold;}
+.dj_a11y .dojoDndAvatarHeader td {padding-left:2px !important;}
+.dj_a11y .dojoDndAvatarHeader td span {padding-right: 5px;}
diff --git a/src/main/resources/static/dojo/resources/dojo.css b/src/main/resources/static/dojo/resources/dojo.css
new file mode 100644
index 0000000000000000000000000000000000000000..fdaa84223bc570a0af337a1ca736a1abaca2b7e1
--- /dev/null
+++ b/src/main/resources/static/dojo/resources/dojo.css
@@ -0,0 +1,196 @@
+/*
+ dojo.css
+ Baseline CSS file for general usage.
+
+ This file is intended to be a "quick and dirty" stylesheet you can use to give
+ a straight-up web page some basic styling without having to do the dirty work
+ yourself. It includes a modified version of YUI's reset.css (we pulled some
+ of the list reset definitions, among other things), and then provides some very
+ basic style rules to be applied to general HTML elements.
+
+ This stylesheet is NOT intended to serve as the foundation for more complex things--
+ including the use of a TABLE for layout purposes. The table definitions in this
+ file make the assumption that you will be using tables for thier declared purpose:
+ displaying tabular data.
+
+ If you are looking for a baseline stylesheet using tables for grid layout, you will
+ need to supply your own layout rules to override the ones in this stylesheet.
+
+ Applications using Dojo will function correctly without including this
+ file, but it should provide sane defaults for many common things that page
+ authors often need to set up manually.
+
+ The Dojo Core uses this stylesheet to quickly style HTML-based tests and demos. Feel
+ free to use it as you will.
+*/
+
+/*****************************************************************************************/
+
+/*
+ The below are borrowed from YUI's reset style sheets for pages and fonts.
+ We've verified w/ the YUI development team that these are entirely
+ copyright Yahoo, written entirely by Nate Koechley and Matt Sweeney without
+ external contributions.
+
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ Code licensed under the BSD License:
+ http://developer.yahoo.net/yui/license.txt
+ version: 2.2.1
+*/
+
+body, div, dl, dt, dd, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset, img {
+ border: 0 none;
+}
+
+address, caption, cite, code, dfn, th, var {
+ font-style: normal;
+ font-weight: normal;
+}
+
+caption, th {
+ text-align: left;
+}
+
+q:before, q:after {
+ content:"";
+}
+
+abbr, acronym {
+ border:0;
+}
+/* End YUI imported code. */
+
+/*****************************************************************************************/
+
+/*
+ Begin Dojo additions.
+
+ Style definitions, based loosely on the Dijit Tundra theme.
+ Relative unit calculations based on "Compose to a Vertical Rhythm",
+ by Richard Rutter (http://24ways.org/2006/compose-to-a-vertical-rhythm)
+
+ If changing the font size, make sure you do it in both
+ percent and px (% for IE, px for everything else).
+ % value based on default size of 16px (in most browsers).
+ So if you want the default size to be 14px, set the
+ % to 87% (14 / 16 = 0.875).
+
+ Typical values:
+ 10px: 62.5%
+ 11px: 69% (68.75)
+ 12px: 75%
+ 13px: 81.25%
+ 14px: 87.5%
+ 16px: 100%
+
+ Default: 12px
+*/
+body {
+ font: 12px Myriad,Helvetica,Tahoma,Arial,clean,sans-serif;
+ *font-size: 75%;
+}
+
+/* Headings */
+h1 {
+ font-size: 1.5em;
+ font-weight: normal;
+ line-height: 1em;
+ margin-top: 1em;
+ margin-bottom:0;
+}
+
+h2 {
+ font-size: 1.1667em;
+ font-weight: bold;
+ line-height: 1.286em;
+ margin-top: 1.929em;
+ margin-bottom:0.643em;
+}
+
+h3, h4, h5, h6 {
+ font-size: 1em;
+ font-weight: bold;
+ line-height: 1.5em;
+ margin-top: 1.5em;
+ margin-bottom: 0;
+}
+
+/* paragraphs, quotes and lists */
+p {
+ font-size: 1em;
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+ line-height: 1.5em;
+}
+
+blockquote {
+ font-size: 0.916em;
+ margin-top: 3.272em;
+ margin-bottom: 3.272em;
+ line-height: 1.636em;
+ padding: 1.636em;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+
+ol li, ul li {
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 0;
+}
+
+/* pre and code */
+pre, code {
+ font-size:115%;
+ *font-size:100%;
+ font-family: Courier, "Courier New";
+ background-color: #efefef;
+ border: 1px solid #ccc;
+}
+
+pre {
+ border-width: 1px 0;
+ padding: 1.5em;
+}
+
+/*
+ Tables
+
+ Note that these table definitions make the assumption that you are using tables
+ to display tabular data, and NOT using tables as layout mechanisms. If you are
+ using tables for layout, you will probably want to override these rules with
+ more specific ones.
+
+ These definitions make tabular data look presentable, particularly when presented
+ inline with paragraphs.
+*/
+table { font-size:100%; }
+
+.dojoTabular {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 1px solid #ccc;
+ margin: 0 1.5em;
+}
+
+.dojoTabular th {
+ text-align: center;
+ font-weight: bold;
+}
+
+.dojoTabular thead,
+.dojoTabular tfoot {
+ background-color: #efefef;
+ border: 1px solid #ccc;
+ border-width: 1px 0;
+}
+
+.dojoTabular th,
+.dojoTabular td {
+ padding: 0.25em 0.5em;
+}
diff --git a/src/main/resources/static/dojo/resources/iframe_history.html b/src/main/resources/static/dojo/resources/iframe_history.html
new file mode 100644
index 0000000000000000000000000000000000000000..2c5acce2d83b5da378adf473266cb581abfe8f32
--- /dev/null
+++ b/src/main/resources/static/dojo/resources/iframe_history.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
The Dojo Toolkit -- iframe_history.html
+
+
This file is used in Dojo's back/fwd button management.
+
+
diff --git a/src/main/resources/static/dojo/resources/images/dndCopy.png b/src/main/resources/static/dojo/resources/images/dndCopy.png
new file mode 100644
index 0000000000000000000000000000000000000000..660ca4fb10fc1a0a26bd6235a45395031d7e634e
Binary files /dev/null and b/src/main/resources/static/dojo/resources/images/dndCopy.png differ
diff --git a/src/main/resources/static/dojo/resources/images/dndMove.png b/src/main/resources/static/dojo/resources/images/dndMove.png
new file mode 100644
index 0000000000000000000000000000000000000000..74af29c08938f718f4238fb5c4f09f6750ae56dc
Binary files /dev/null and b/src/main/resources/static/dojo/resources/images/dndMove.png differ
diff --git a/src/main/resources/static/dojo/resources/images/dndNoCopy.png b/src/main/resources/static/dojo/resources/images/dndNoCopy.png
new file mode 100644
index 0000000000000000000000000000000000000000..998c2f2692201457b3c310e24b13e834b5b9c4c9
Binary files /dev/null and b/src/main/resources/static/dojo/resources/images/dndNoCopy.png differ
diff --git a/src/main/resources/static/dojo/resources/images/dndNoMove.png b/src/main/resources/static/dojo/resources/images/dndNoMove.png
new file mode 100644
index 0000000000000000000000000000000000000000..e909173e0df652ae3585c6f9e5024749761bfc60
Binary files /dev/null and b/src/main/resources/static/dojo/resources/images/dndNoMove.png differ
diff --git a/src/main/resources/static/dojo/robot.js b/src/main/resources/static/dojo/robot.js
new file mode 100644
index 0000000000000000000000000000000000000000..4e5ba706a71d808ec201bf941bec4eef5a70832e
--- /dev/null
+++ b/src/main/resources/static/dojo/robot.js
@@ -0,0 +1,170 @@
+define([
+ "./_base/array",
+ "./dom",
+ "./dom-geometry",
+ "./_base/kernel",
+ "./_base/lang",
+ "./_base/window",
+ "doh/_browserRunner",
+ "doh/robot",
+ "./window"
+], function(array, dom, geom, kernel, lang, win, doh, robot, winUtils){
+
+kernel.experimental("dojo.robot");
+
+// users who use doh+dojo get the added convenience of robot.mouseMoveAt(),
+// instead of computing the absolute coordinates of their elements themselves
+lang.mixin(robot, {
+
+ _resolveNode: function(/*String||DOMNode||Function*/ n){
+ if(typeof n == "function"){
+ // if the user passed a function returning a node, evaluate it
+ n = n();
+ }
+ return n ? dom.byId(n) : null;
+ },
+
+ _scrollIntoView: function(/*Node*/ n){
+ // scrolls the passed node into view, scrolling all ancestor frames/windows as well.
+ // Assumes parent iframes can be made fully visible given the current browser window size
+ var p = null;
+ array.forEach(robot._getWindowChain(n), function(w){
+ // get the position of the node wrt its parent window
+ // if it is a parent frame, its padding and border extents will get added in
+ var p2 = geom.position(n, false),
+ b = geom.getPadBorderExtents(n),
+ oldp = null;
+ // if p2 is the position of the original passed node, store the position away as p
+ // otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents
+ if(!p){
+ p = p2;
+ }else{
+ oldp = p;
+ p = {x: p.x+p2.x+b.l,
+ y: p.y+p2.y+b.t,
+ w: p.w,
+ h: p.h};
+
+ }
+ // scroll the parent window so that the node translated into the parent window's coordinate space is in view
+ winUtils.scrollIntoView(n,p);
+ // adjust position for the new scroll offsets
+ p2 = geom.position(n, false);
+ if(!oldp){
+ p = p2;
+ }else{
+ p = {x: oldp.x+p2.x+b.l,
+ y: oldp.y+p2.y+b.t,
+ w: p.w,
+ h: p.h};
+ }
+ // get the parent iframe so it can be scrolled too
+ n = w.frameElement;
+ });
+ },
+
+ _position: function(/*Node*/ n){
+ // Returns the geom.position of the passed node wrt the passed window's viewport,
+ // following any parent iframes containing the node and clipping the node to each iframe.
+ // precondition: _scrollIntoView already called
+ var p = null, max = Math.max, min = Math.min;
+ // p: the returned position of the node
+ array.forEach(robot._getWindowChain(n), function(w){
+ // get the position of the node wrt its parent window
+ // if it is a parent frame, its padding and border extents will get added in
+ var p2 = geom.position(n, false), b = geom.getPadBorderExtents(n);
+ // if p2 is the position of the original passed node, store the position away as p
+ // otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents
+ if(!p){
+ p = p2;
+ }else{
+ var view = winUtils.getBox(n.contentWindow.document);
+ p2.r = p2.x+view.w;
+ p2.b = p2.y+view.h;
+ p = {x: max(p.x+p2.x,p2.x)+b.l, // clip left edge of node wrt the iframe
+ y: max(p.y+p2.y,p2.y)+b.t, // top edge
+ r: min(p.x+p2.x+p.w,p2.r)+b.l, // right edge (to compute width)
+ b: min(p.y+p2.y+p.h,p2.b)+b.t}; // bottom edge (to compute height)
+ // save a few bytes by computing width and height from r and b
+ p.w = p.r-p.x;
+ p.h = p.b-p.y;
+ }
+ // the new node is now the old node's parent iframe
+ n=w.frameElement;
+ });
+ return p;
+ },
+
+ _getWindowChain : function(/*Node*/ n){
+ // Returns an array of windows starting from the passed node's parent window and ending at dojo's window
+ var cW = winUtils.get(n.ownerDocument);
+ var arr = [cW];
+ var f = cW.frameElement;
+ return (cW == win.global || !f) ? arr : arr.concat(robot._getWindowChain(f));
+ },
+
+ scrollIntoView : function(/*String||DOMNode||Function*/ node, /*Number, optional*/ delay){
+ // summary:
+ // Scroll the passed node into view, if it is not.
+ // node:
+ // The id of the node, or the node itself, to move the mouse to.
+ // If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes.
+ // This is useful if you need to move the mouse to an node that is not yet present.
+ // delay:
+ // Delay, in milliseconds, to wait before firing.
+ // The delay is a delta with respect to the previous automation call.
+
+ robot.sequence(function(){
+ robot._scrollIntoView(robot._resolveNode(node));
+ }, delay);
+ },
+
+ mouseMoveAt : function(/*String||DOMNode||Function*/ node, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Number, optional*/ offsetX, /*Number, optional*/ offsetY){
+ // summary:
+ // Moves the mouse over the specified node at the specified relative x,y offset.
+ // description:
+ // Moves the mouse over the specified node at the specified relative x,y offset.
+ // If you do not specify an offset, mouseMove will default to move to the middle of the node.
+ // Example: to move the mouse over a ComboBox's down arrow node, call doh.mouseMoveAt(dijit.byId('setvaluetest').downArrowNode);
+ // node:
+ // The id of the node, or the node itself, to move the mouse to.
+ // If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes.
+ // This is useful if you need to move the mouse to an node that is not yet present.
+ // delay:
+ // Delay, in milliseconds, to wait before firing.
+ // The delay is a delta with respect to the previous automation call.
+ // For example, the following code ends after 600ms:
+ // | robot.mouseClick({left:true}, 100) // first call; wait 100ms
+ // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
+ // duration:
+ // Approximate time Robot will spend moving the mouse
+ // The default is 100ms.
+ // offsetX:
+ // x offset relative to the node, in pixels, to move the mouse. The default is half the node's width.
+ // offsetY:
+ // y offset relative to the node, in pixels, to move the mouse. The default is half the node's height.
+
+ robot._assertRobot();
+
+ // Schedule an action to scroll the node into view, then calculate it's center point
+ var point = {};
+ this.sequence(function(){
+ node = robot._resolveNode(node);
+ robot._scrollIntoView(node);
+ var pos = robot._position(node);
+ if(offsetY === undefined){
+ offsetX = pos.w/2;
+ offsetY = pos.h/2;
+ }
+ point.x = pos.x+offsetX;
+ point.y = pos.y+offsetY;
+ }, delay);
+
+ // Schedule a bunch of actions to move the mouse from the current position to point.
+ // These actions won't run until after the above callback.
+ this.mouseMoveTo(point, 0, duration, false);
+ }
+});
+
+return robot;
+});
diff --git a/src/main/resources/static/dojo/robotx.js b/src/main/resources/static/dojo/robotx.js
new file mode 100644
index 0000000000000000000000000000000000000000..c376a8850dc0c07932d83d5cf85c1b98c01abc48
--- /dev/null
+++ b/src/main/resources/static/dojo/robotx.js
@@ -0,0 +1,171 @@
+define([
+ "require",
+ "doh/main",
+ "./aspect",
+ "./dom-construct",
+ "./dom-style",
+ "./_base/kernel",
+ "./_base/lang",
+ "./on",
+ "./robot",
+ "./sniff",
+ "./_base/window"
+], function(require, doh, aspect, construct, style, kernel, lang, on, robot, has, win){
+
+kernel.experimental("dojo.robotx");
+
+// module:
+// dojo.robotx
+// description:
+// loads an external app into an iframe and points dojo.doc to the iframe document, allowing the robot to control it
+// to use: set robotURL in djConfig to the URL you want to load
+// dojo.require this file
+
+// The iframe containing the external app
+var iframe = null;
+
+// On IE6/7, a firebug console will appear. Scrunch it a bit to leave room for the external test file.
+kernel.config.debugHeight = kernel.config.debugHeight || 200;
+
+
+// urlLoaded is a Deferred that will be resolved whenever the iframe passed to initRobot() finishes loading, or reloads
+var urlLoaded;
+
+function attachIframe(url){
+ // summary:
+ // Create iframe to load external app at specified url. Iframe gets onload handler to call onIframeLoad()
+ // when specified URL finishes loading, and also if the iframe loads a different URL in the future.
+ // returns:
+ // A Deferred that fires when everything has finished initializing
+
+ require(["./domReady!"], function(){
+ var emptyStyle = {
+ overflow: "hidden",
+ margin: "0px",
+ borderWidth: "0px",
+ height: "100%",
+ width: "100%"
+ };
+ style.set(document.documentElement, emptyStyle);
+ style.set(document.body, emptyStyle);
+
+ // Create the iframe for the external document. Put it above the firebug-lite div (if such a div exists).
+ // console.log("creating iframe for external document");
+ iframe = document.createElement("iframe");
+ iframe.setAttribute("ALLOWTRANSPARENCY","true");
+ iframe.scrolling = has("ie") ? "yes" : "auto";
+ var scrollRoot = document.compatMode == "BackCompat" ? document.body : document.documentElement;
+ var consoleHeight = (document.getElementById("firebug") || {}).offsetHeight || 0;
+ style.set(iframe, {
+ border: "0px none",
+ padding: "0px",
+ margin: "0px",
+ width: "100%",
+ height: consoleHeight ? (scrollRoot.clientHeight - consoleHeight)+"px" : "100%"
+ });
+ iframe.src = url;
+
+ // Code to handle load event on iframe. Seems like this should happen before setting iframe src on line above?
+ // Also, can't we use on() in all cases, even for old IE?
+ if(iframe.attachEvent !== undefined){
+ iframe.attachEvent("onload", onIframeLoad);
+ }else{
+ on(iframe, "load", onIframeLoad);
+ }
+
+ construct.place(iframe, win.body(), "first");
+ });
+}
+
+function onIframeLoad(){
+ // summary:
+ // Load handler when iframe specified to initRobot() finishes loading, or when it reloads.
+ // It resolves the urlLoaded Deferred to make the rests of the tests runs.
+
+ robot._updateDocument();
+
+ // If dojo is present in the test case, then at least make a best effort to wait for it to load.
+ // The test must handle other race conditions like initial data queries or asynchronous parses by itself.
+ if(iframe.contentWindow.require){
+ iframe.contentWindow.require(["dojo/ready"], function(ready){
+ ready(Infinity, function(){
+ setTimeout(function(){
+ urlLoaded.resolve(true);
+ }, 500); // 500ms fudge factor; otherwise focus doesn't work on IE8, see ValidationTextBox.js, TimeTextBox.js, etc.
+ });
+ });
+ }else{
+ urlLoaded.resolve(true);
+ }
+}
+
+lang.mixin(robot, {
+ _updateDocument: function(){
+ // summary:
+ // Called every time a new page is loaded into the iframe, to setup variables
+ // Point dojo.global, dojo.publish, etc. to refer to iframe.
+ // Remove for 2.0?
+
+ kernel.setContext(iframe.contentWindow, iframe.contentWindow.document);
+
+ // Also set pointers inside robot, for easy access via AMD (where there is no dojo variable)
+ robot.window = iframe.contentWindow;
+ robot.doc = iframe.contentWindow.document;
+
+ // TODO: shouldn't this wait until dojo has finished loading in the iframe? See require code in onIframeLoad().
+ var win = kernel.global;
+ if(win.dojo){
+ // allow the tests to subscribe to topics published by the iframe
+ kernel.publish = win.dojo.publish;
+ kernel.subscribe = win.dojo.subscribe;
+ kernel.connectPublisher = win.dojo.connectPublisher;
+ }
+ },
+
+ initRobot: function(/*String*/ url){
+ // summary:
+ // Opens the application at the specified URL for testing, redirecting dojo to point to the application
+ // environment instead of the test environment.
+ // url:
+ // URL to open. Any of the test's dojo.doc calls (e.g. dojo.byId()), and any dijit.registry calls
+ // (e.g. dijit.byId()) will point to elements and widgets inside this application.
+
+ doh.registerGroup("initialize robot", {
+ name: "load " + url,
+ timeout: 100000, // could take more than 10s so setting to 100s
+ runTest: function(){
+ // Setup module level urlLoaded Deferred that will be resolved by onIframeLoad(), after the iframe
+ // has finished loading
+ urlLoaded = new doh.Deferred();
+ attachIframe(url);
+
+ return urlLoaded;
+ }
+ });
+ },
+
+ waitForPageToLoad: function(/*Function*/ submitActions){
+ // summary:
+ // Notifies DOH that the doh.robot is about to make a page change in the application it is driving,
+ // returning a doh.Deferred object the user should return in their runTest function as part of a DOH test.
+ // example:
+ // | runTest: function(){
+ // | return waitForPageLoad(function(){ doh.robot.keyPress(keys.ENTER, 500); });
+ // | }
+ // submitActions:
+ // The doh.robot will execute the actions the test passes into the submitActions argument (like clicking the submit button),
+ // expecting these actions to create a page change (like a form submit).
+ // After these actions execute and the resulting page loads, the next test will start.
+
+ // Setup a new Deferred that onIframeLoad() will resolve when the iframe finishes loading
+ urlLoaded = new doh.Deferred();
+
+ submitActions();
+
+ return urlLoaded;
+ }
+
+});
+
+return robot;
+});
diff --git a/src/main/resources/static/dojo/router.js b/src/main/resources/static/dojo/router.js
new file mode 100644
index 0000000000000000000000000000000000000000..1a0e73f043ad029fd3e64fd56379854265cc89af
--- /dev/null
+++ b/src/main/resources/static/dojo/router.js
@@ -0,0 +1,28 @@
+define([
+ "./router/RouterBase"
+], function(RouterBase){
+
+ // module:
+ // dojo/router
+
+/*=====
+return {
+ // summary:
+ // A singleton-style instance of dojo/router/RouterBase. See that
+ // module for specifics.
+ // example:
+ // | router.register("/widgets/:id", function(evt){
+ // | // If "/widgets/3" was matched,
+ // | // evt.params.id === "3"
+ // | xhr.get({
+ // | url: "/some/path/" + evt.params.id,
+ // | load: function(data){
+ // | // ...
+ // | }
+ // | });
+ // | });
+};
+=====*/
+
+ return new RouterBase({});
+});
diff --git a/src/main/resources/static/dojo/router/RouterBase.js b/src/main/resources/static/dojo/router/RouterBase.js
new file mode 100644
index 0000000000000000000000000000000000000000..05e787324c54a3a10a7b9faa7698a158ce522824
--- /dev/null
+++ b/src/main/resources/static/dojo/router/RouterBase.js
@@ -0,0 +1,376 @@
+define([
+ "dojo/_base/declare",
+ "dojo/hash",
+ "dojo/topic"
+], function(declare, hash, topic){
+
+ // module:
+ // dojo/router/RouterBase
+
+ // Creating a basic trim to avoid needing the full dojo/string module
+ // similarly to dojo/_base/lang's trim
+ var trim;
+ if(String.prototype.trim){
+ trim = function(str){ return str.trim(); };
+ }else{
+ trim = function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); };
+ }
+
+ // Firing of routes on the route object is always the same,
+ // no clean way to expose this on the prototype since it's for the
+ // internal router objects.
+ function fireRoute(params, currentPath, newPath){
+ var queue, isStopped, isPrevented, eventObj, callbackArgs, i, l;
+
+ queue = this.callbackQueue;
+ isStopped = false;
+ isPrevented = false;
+ eventObj = {
+ stopImmediatePropagation: function(){ isStopped = true; },
+ preventDefault: function(){ isPrevented = true; },
+ oldPath: currentPath,
+ newPath: newPath,
+ params: params
+ };
+
+ callbackArgs = [eventObj];
+
+ if(params instanceof Array){
+ callbackArgs = callbackArgs.concat(params);
+ }else{
+ for(var key in params){
+ callbackArgs.push(params[key]);
+ }
+ }
+
+ for(i=0, l=queue.length; i 0 ? parameterNames : null;
+ },
+
+ _indexRoutes: function(){
+ var i, l, route, routeIndex, routes = this._routes;
+
+ // Start a new route index
+ routeIndex = this._routeIndex = {};
+
+ // Set it up again
+ for(i=0, l=routes.length; i= 0){
+ // if we end with a ">", "+", or "~", that means we're implicitly
+ // searching all children, so make it explicit
+ query += " * ";
+ }else{
+ // if you have not provided a terminator, one will be provided for
+ // you...
+ query += " ";
+ }
+
+ var ts = function(/*Integer*/ s, /*Integer*/ e){
+ // trim and slice.
+
+ // take an index to start a string slice from and an end position
+ // and return a trimmed copy of that sub-string
+ return trim(query.slice(s, e));
+ };
+
+ // the overall data graph of the full query, as represented by queryPart objects
+ var queryParts = [];
+
+
+ // state keeping vars
+ var inBrackets = -1, inParens = -1, inMatchFor = -1,
+ inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
+ lc = "", cc = "", pStart;
+
+ // iteration vars
+ var x = 0, // index in the query
+ ql = query.length,
+ currentPart = null, // data structure representing the entire clause
+ _cp = null; // the current pseudo or attr matcher
+
+ // several temporary variables are assigned to this structure during a
+ // potential sub-expression match:
+ // attr:
+ // a string representing the current full attribute match in a
+ // bracket expression
+ // type:
+ // if there's an operator in a bracket expression, this is
+ // used to keep track of it
+ // value:
+ // the internals of parenthetical expression for a pseudo. for
+ // :nth-child(2n+1), value might be "2n+1"
+
+ var endTag = function(){
+ // called when the tokenizer hits the end of a particular tag name.
+ // Re-sets state variables for tag matching and sets up the matcher
+ // to handle the next type of token (tag or operator).
+ if(inTag >= 0){
+ var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
+ currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
+ inTag = -1;
+ }
+ };
+
+ var endId = function(){
+ // called when the tokenizer might be at the end of an ID portion of a match
+ if(inId >= 0){
+ currentPart.id = ts(inId, x).replace(/\\/g, "");
+ inId = -1;
+ }
+ };
+
+ var endClass = function(){
+ // called when the tokenizer might be at the end of a class name
+ // match. CSS allows for multiple classes, so we augment the
+ // current item with another class in its list
+ if(inClass >= 0){
+ currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
+ inClass = -1;
+ }
+ };
+
+ var endAll = function(){
+ // at the end of a simple fragment, so wall off the matches
+ endId();
+ endTag();
+ endClass();
+ };
+
+ var endPart = function(){
+ endAll();
+ if(inPseudo >= 0){
+ currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
+ }
+ // hint to the selector engine to tell it whether or not it
+ // needs to do any iteration. Many simple selectors don't, and
+ // we can avoid significant construction-time work by advising
+ // the system to skip them
+ currentPart.loops = (
+ currentPart.pseudos.length ||
+ currentPart.attrs.length ||
+ currentPart.classes.length );
+
+ currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
+
+
+ // otag/tag are hints to suggest to the system whether or not
+ // it's an operator or a tag. We save a copy of otag since the
+ // tag name is cast to upper-case in regular HTML matches. The
+ // system has a global switch to figure out if the current
+ // expression needs to be case sensitive or not and it will use
+ // otag or tag accordingly
+ currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
+
+ if(currentPart.tag){
+ // if we're in a case-insensitive HTML doc, we likely want
+ // the toUpperCase when matching on element.tagName. If we
+ // do it here, we can skip the string op per node
+ // comparison
+ currentPart.tag = currentPart.tag.toUpperCase();
+ }
+
+ // add the part to the list
+ if(queryParts.length && (queryParts[queryParts.length-1].oper)){
+ // operators are always infix, so we remove them from the
+ // list and attach them to the next match. The evaluator is
+ // responsible for sorting out how to handle them.
+ currentPart.infixOper = queryParts.pop();
+ currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
+ /*
+ console.debug( "swapping out the infix",
+ currentPart.infixOper,
+ "and attaching it to",
+ currentPart);
+ */
+ }
+ queryParts.push(currentPart);
+
+ currentPart = null;
+ };
+
+ // iterate over the query, character by character, building up a
+ // list of query part objects
+ for(; lc=cc, cc=query.charAt(x), x < ql; x++){
+ // cc: the current character in the match
+ // lc: the last character (if any)
+
+ // someone is trying to escape something, so don't try to match any
+ // fragments. We assume we're inside a literal.
+ if(lc == "\\"){ continue; }
+ if(!currentPart){ // a part was just ended or none has yet been created
+ // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
+ pStart = x;
+ // rules describe full CSS sub-expressions, like:
+ // #someId
+ // .className:first-child
+ // but not:
+ // thinger > div.howdy[type=thinger]
+ // the indidual components of the previous query would be
+ // split into 3 parts that would be represented a structure like:
+ // [
+ // {
+ // query: "thinger",
+ // tag: "thinger",
+ // },
+ // {
+ // query: "div.howdy[type=thinger]",
+ // classes: ["howdy"],
+ // infixOper: {
+ // query: ">",
+ // oper: ">",
+ // }
+ // },
+ // ]
+ currentPart = {
+ query: null, // the full text of the part's rule
+ pseudos: [], // CSS supports multiple pseud-class matches in a single rule
+ attrs: [], // CSS supports multi-attribute match, so we need an array
+ classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
+ tag: null, // only one tag...
+ oper: null, // ...or operator per component. Note that these wind up being exclusive.
+ id: null, // the id component of a rule
+ getTag: function(){
+ return caseSensitive ? this.otag : this.tag;
+ }
+ };
+
+ // if we don't have a part, we assume we're going to start at
+ // the beginning of a match, which should be a tag name. This
+ // might fault a little later on, but we detect that and this
+ // iteration will still be fine.
+ inTag = x;
+ }
+
+ // Skip processing all quoted characters.
+ // If we are inside quoted text then currentQuoteChar stores the character that began the quote,
+ // thus that character that will end it.
+ if(currentQuoteChar){
+ if(cc == currentQuoteChar){
+ currentQuoteChar = null;
+ }
+ continue;
+ }else if (cc == "'" || cc == '"'){
+ currentQuoteChar = cc;
+ continue;
+ }
+
+ if(inBrackets >= 0){
+ // look for a the close first
+ if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
+ if(!_cp.attr){
+ // no attribute match was previously begun, so we
+ // assume this is an attribute existence match in the
+ // form of [someAttributeName]
+ _cp.attr = ts(inBrackets+1, x);
+ }else{
+ // we had an attribute already, so we know that we're
+ // matching some sort of value, as in [attrName=howdy]
+ _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
+ }
+ var cmf = _cp.matchFor;
+ if(cmf){
+ // try to strip quotes from the matchFor value. We want
+ // [attrName=howdy] to match the same
+ // as [attrName = 'howdy' ]
+ if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
+ _cp.matchFor = cmf.slice(1, -1);
+ }
+ }
+ // remove backslash escapes from an attribute match, since DOM
+ // querying will get attribute values without backslashes
+ if(_cp.matchFor){
+ _cp.matchFor = _cp.matchFor.replace(/\\/g, "");
+ }
+
+ // end the attribute by adding it to the list of attributes.
+ currentPart.attrs.push(_cp);
+ _cp = null; // necessary?
+ inBrackets = inMatchFor = -1;
+ }else if(cc == "="){
+ // if the last char was an operator prefix, make sure we
+ // record it along with the "=" operator.
+ var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
+ _cp.type = addToCc+cc;
+ _cp.attr = ts(inBrackets+1, x-addToCc.length);
+ inMatchFor = x+1;
+ }
+ // now look for other clause parts
+ }else if(inParens >= 0){
+ // if we're in a parenthetical expression, we need to figure
+ // out if it's attached to a pseudo-selector rule like
+ // :nth-child(1)
+ if(cc == ")"){
+ if(inPseudo >= 0){
+ _cp.value = ts(inParens+1, x);
+ }
+ inPseudo = inParens = -1;
+ }
+ }else if(cc == "#"){
+ // start of an ID match
+ endAll();
+ inId = x+1;
+ }else if(cc == "."){
+ // start of a class match
+ endAll();
+ inClass = x;
+ }else if(cc == ":"){
+ // start of a pseudo-selector match
+ endAll();
+ inPseudo = x;
+ }else if(cc == "["){
+ // start of an attribute match.
+ endAll();
+ inBrackets = x;
+ // provide a new structure for the attribute match to fill-in
+ _cp = {
+ /*=====
+ attr: null, type: null, matchFor: null
+ =====*/
+ };
+ }else if(cc == "("){
+ // we really only care if we've entered a parenthetical
+ // expression if we're already inside a pseudo-selector match
+ if(inPseudo >= 0){
+ // provide a new structure for the pseudo match to fill-in
+ _cp = {
+ name: ts(inPseudo+1, x),
+ value: null
+ };
+ currentPart.pseudos.push(_cp);
+ }
+ inParens = x;
+ }else if(
+ (cc == " ") &&
+ // if it's a space char and the last char is too, consume the
+ // current one without doing more work
+ (lc != cc)
+ ){
+ endPart();
+ }
+ }
+ return queryParts;
+ };
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // DOM query infrastructure
+ ////////////////////////////////////////////////////////////////////////
+
+ var agree = function(first, second){
+ // the basic building block of the yes/no chaining system. agree(f1,
+ // f2) generates a new function which returns the boolean results of
+ // both of the passed functions to a single logical-anded result. If
+ // either are not passed, the other is used exclusively.
+ if(!first){ return second; }
+ if(!second){ return first; }
+
+ return function(){
+ return first.apply(window, arguments) && second.apply(window, arguments);
+ };
+ };
+
+ var getArr = function(i, arr){
+ // helps us avoid array alloc when we don't need it
+ var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
+ if(i){ r.push(i); }
+ return r;
+ };
+
+ var _isElement = function(n){ return (1 == n.nodeType); };
+
+ // FIXME: need to coalesce _getAttr with defaultGetter
+ var blank = "";
+ var _getAttr = function(elem, attr){
+ if(!elem){ return blank; }
+ if(attr == "class"){
+ return elem.className || blank;
+ }
+ if(attr == "for"){
+ return elem.htmlFor || blank;
+ }
+ if(attr == "style"){
+ return elem.style.cssText || blank;
+ }
+ return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
+ };
+
+ var attrs = {
+ "*=": function(attr, value){
+ return function(elem){
+ // E[foo*="bar"]
+ // an E element whose "foo" attribute value contains
+ // the substring "bar"
+ return (_getAttr(elem, attr).indexOf(value)>=0);
+ };
+ },
+ "^=": function(attr, value){
+ // E[foo^="bar"]
+ // an E element whose "foo" attribute value begins exactly
+ // with the string "bar"
+ return function(elem){
+ return (_getAttr(elem, attr).indexOf(value)==0);
+ };
+ },
+ "$=": function(attr, value){
+ // E[foo$="bar"]
+ // an E element whose "foo" attribute value ends exactly
+ // with the string "bar"
+ return function(elem){
+ var ea = " "+_getAttr(elem, attr);
+ var lastIndex = ea.lastIndexOf(value);
+ return lastIndex > -1 && (lastIndex==(ea.length-value.length));
+ };
+ },
+ "~=": function(attr, value){
+ // E[foo~="bar"]
+ // an E element whose "foo" attribute value is a list of
+ // space-separated values, one of which is exactly equal
+ // to "bar"
+
+ // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
+ var tval = " "+value+" ";
+ return function(elem){
+ var ea = " "+_getAttr(elem, attr)+" ";
+ return (ea.indexOf(tval)>=0);
+ };
+ },
+ "|=": function(attr, value){
+ // E[hreflang|="en"]
+ // an E element whose "hreflang" attribute has a
+ // hyphen-separated list of values beginning (from the
+ // left) with "en"
+ var valueDash = value+"-";
+ return function(elem){
+ var ea = _getAttr(elem, attr);
+ return (
+ (ea == value) ||
+ (ea.indexOf(valueDash)==0)
+ );
+ };
+ },
+ "=": function(attr, value){
+ return function(elem){
+ return (_getAttr(elem, attr) == value);
+ };
+ }
+ };
+
+ // avoid testing for node type if we can. Defining this in the negative
+ // here to avoid negation in the fast path.
+ var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
+ var _ns = !_noNES ? "nextElementSibling" : "nextSibling";
+ var _ps = !_noNES ? "previousElementSibling" : "previousSibling";
+ var _simpleNodeTest = (_noNES ? _isElement : yesman);
+
+ var _lookLeft = function(node){
+ // look left
+ while(node = node[_ps]){
+ if(_simpleNodeTest(node)){ return false; }
+ }
+ return true;
+ };
+
+ var _lookRight = function(node){
+ // look right
+ while(node = node[_ns]){
+ if(_simpleNodeTest(node)){ return false; }
+ }
+ return true;
+ };
+
+ var getNodeIndex = function(node){
+ var root = node.parentNode;
+ root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
+ var i = 0,
+ tret = root.children || root.childNodes,
+ ci = (node["_i"]||node.getAttribute("_i")||-1),
+ cl = (root["_l"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l") : -1));
+
+ if(!tret){ return -1; }
+ var l = tret.length;
+
+ // we calculate the parent length as a cheap way to invalidate the
+ // cache. It's not 100% accurate, but it's much more honest than what
+ // other libraries do
+ if( cl == l && ci >= 0 && cl >= 0 ){
+ // if it's legit, tag and release
+ return ci;
+ }
+
+ // else re-key things
+ if(has("ie") && typeof root.setAttribute !== "undefined"){
+ root.setAttribute("_l", l);
+ }else{
+ root["_l"] = l;
+ }
+ ci = -1;
+ for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
+ if(_simpleNodeTest(te)){
+ if(has("ie")){
+ te.setAttribute("_i", ++i);
+ }else{
+ te["_i"] = ++i;
+ }
+ if(node === te){
+ // NOTE:
+ // shortcutting the return at this step in indexing works
+ // very well for benchmarking but we avoid it here since
+ // it leads to potential O(n^2) behavior in sequential
+ // getNodexIndex operations on a previously un-indexed
+ // parent. We may revisit this at a later time, but for
+ // now we just want to get the right answer more often
+ // than not.
+ ci = i;
+ }
+ }
+ }
+ return ci;
+ };
+
+ var isEven = function(elem){
+ return !((getNodeIndex(elem)) % 2);
+ };
+
+ var isOdd = function(elem){
+ return ((getNodeIndex(elem)) % 2);
+ };
+
+ var pseudos = {
+ "checked": function(name, condition){
+ return function(elem){
+ return !!("checked" in elem ? elem.checked : elem.selected);
+ };
+ },
+ "disabled": function(name, condition){
+ return function(elem){
+ return elem.disabled;
+ };
+ },
+ "enabled": function(name, condition){
+ return function(elem){
+ return !elem.disabled;
+ };
+ },
+ "first-child": function(){ return _lookLeft; },
+ "last-child": function(){ return _lookRight; },
+ "only-child": function(name, condition){
+ return function(node){
+ return _lookLeft(node) && _lookRight(node);
+ };
+ },
+ "empty": function(name, condition){
+ return function(elem){
+ // DomQuery and jQuery get this wrong, oddly enough.
+ // The CSS 3 selectors spec is pretty explicit about it, too.
+ var cn = elem.childNodes;
+ var cnl = elem.childNodes.length;
+ // if(!cnl){ return true; }
+ for(var x=cnl-1; x >= 0; x--){
+ var nt = cn[x].nodeType;
+ if((nt === 1)||(nt == 3)){ return false; }
+ }
+ return true;
+ };
+ },
+ "contains": function(name, condition){
+ var cz = condition.charAt(0);
+ if( cz == '"' || cz == "'" ){ //remove quote
+ condition = condition.slice(1, -1);
+ }
+ return function(elem){
+ return (elem.innerHTML.indexOf(condition) >= 0);
+ };
+ },
+ "not": function(name, condition){
+ var p = getQueryParts(condition)[0];
+ var ignores = { el: 1 };
+ if(p.tag != "*"){
+ ignores.tag = 1;
+ }
+ if(!p.classes.length){
+ ignores.classes = 1;
+ }
+ var ntf = getSimpleFilterFunc(p, ignores);
+ return function(elem){
+ return (!ntf(elem));
+ };
+ },
+ "nth-child": function(name, condition){
+ var pi = parseInt;
+ // avoid re-defining function objects if we can
+ if(condition == "odd"){
+ return isOdd;
+ }else if(condition == "even"){
+ return isEven;
+ }
+ // FIXME: can we shorten this?
+ if(condition.indexOf("n") != -1){
+ var tparts = condition.split("n", 2);
+ var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
+ var idx = tparts[1] ? pi(tparts[1]) : 0;
+ var lb = 0, ub = -1;
+ if(pred > 0){
+ if(idx < 0){
+ idx = (idx % pred) && (pred + (idx % pred));
+ }else if(idx>0){
+ if(idx >= pred){
+ lb = idx - idx % pred;
+ }
+ idx = idx % pred;
+ }
+ }else if(pred<0){
+ pred *= -1;
+ // idx has to be greater than 0 when pred is negative;
+ // shall we throw an error here?
+ if(idx > 0){
+ ub = idx;
+ idx = idx % pred;
+ }
+ }
+ if(pred > 0){
+ return function(elem){
+ var i = getNodeIndex(elem);
+ return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
+ };
+ }else{
+ condition = idx;
+ }
+ }
+ var ncount = pi(condition);
+ return function(elem){
+ return (getNodeIndex(elem) == ncount);
+ };
+ }
+ };
+
+ var defaultGetter = (has("ie") < 9 || has("ie") == 9 && has("quirks")) ? function(cond){
+ var clc = cond.toLowerCase();
+ if(clc == "class"){ cond = "className"; }
+ return function(elem){
+ return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
+ };
+ } : function(cond){
+ return function(elem){
+ return (elem && elem.getAttribute && elem.hasAttribute(cond));
+ };
+ };
+
+ var getSimpleFilterFunc = function(query, ignores){
+ // generates a node tester function based on the passed query part. The
+ // query part is one of the structures generated by the query parser
+ // when it creates the query AST. The "ignores" object specifies which
+ // (if any) tests to skip, allowing the system to avoid duplicating
+ // work where it may have already been taken into account by other
+ // factors such as how the nodes to test were fetched in the first
+ // place
+ if(!query){ return yesman; }
+ ignores = ignores||{};
+
+ var ff = null;
+
+ if(!("el" in ignores)){
+ ff = agree(ff, _isElement);
+ }
+
+ if(!("tag" in ignores)){
+ if(query.tag != "*"){
+ ff = agree(ff, function(elem){
+ return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
+ });
+ }
+ }
+
+ if(!("classes" in ignores)){
+ each(query.classes, function(cname, idx, arr){
+ // get the class name
+ /*
+ var isWildcard = cname.charAt(cname.length-1) == "*";
+ if(isWildcard){
+ cname = cname.substr(0, cname.length-1);
+ }
+ // I dislike the regex thing, even if memoized in a cache, but it's VERY short
+ var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
+ */
+ var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)");
+ ff = agree(ff, function(elem){
+ return re.test(elem.className);
+ });
+ ff.count = idx;
+ });
+ }
+
+ if(!("pseudos" in ignores)){
+ each(query.pseudos, function(pseudo){
+ var pn = pseudo.name;
+ if(pseudos[pn]){
+ ff = agree(ff, pseudos[pn](pn, pseudo.value));
+ }
+ });
+ }
+
+ if(!("attrs" in ignores)){
+ each(query.attrs, function(attr){
+ var matcher;
+ var a = attr.attr;
+ // type, attr, matchFor
+ if(attr.type && attrs[attr.type]){
+ matcher = attrs[attr.type](a, attr.matchFor);
+ }else if(a.length){
+ matcher = defaultGetter(a);
+ }
+ if(matcher){
+ ff = agree(ff, matcher);
+ }
+ });
+ }
+
+ if(!("id" in ignores)){
+ if(query.id){
+ ff = agree(ff, function(elem){
+ return (!!elem && (elem.id == query.id));
+ });
+ }
+ }
+
+ if(!ff){
+ if(!("default" in ignores)){
+ ff = yesman;
+ }
+ }
+ return ff;
+ };
+
+ var _nextSibling = function(filterFunc){
+ return function(node, ret, bag){
+ while(node = node[_ns]){
+ if(_noNES && (!_isElement(node))){ continue; }
+ if(
+ (!bag || _isUnique(node, bag)) &&
+ filterFunc(node)
+ ){
+ ret.push(node);
+ }
+ break;
+ }
+ return ret;
+ };
+ };
+
+ var _nextSiblings = function(filterFunc){
+ return function(root, ret, bag){
+ var te = root[_ns];
+ while(te){
+ if(_simpleNodeTest(te)){
+ if(bag && !_isUnique(te, bag)){
+ break;
+ }
+ if(filterFunc(te)){
+ ret.push(te);
+ }
+ }
+ te = te[_ns];
+ }
+ return ret;
+ };
+ };
+
+ // get an array of child *elements*, skipping text and comment nodes
+ var _childElements = function(filterFunc){
+ filterFunc = filterFunc||yesman;
+ return function(root, ret, bag){
+ // get an array of child elements, skipping text and comment nodes
+ var te, x = 0, tret = root.children || root.childNodes;
+ while(te = tret[x++]){
+ if(
+ _simpleNodeTest(te) &&
+ (!bag || _isUnique(te, bag)) &&
+ (filterFunc(te, x))
+ ){
+ ret.push(te);
+ }
+ }
+ return ret;
+ };
+ };
+
+ // test to see if node is below root
+ var _isDescendant = function(node, root){
+ var pn = node.parentNode;
+ while(pn){
+ if(pn == root){
+ break;
+ }
+ pn = pn.parentNode;
+ }
+ return !!pn;
+ };
+
+ var _getElementsFuncCache = {};
+
+ var getElementsFunc = function(query){
+ var retFunc = _getElementsFuncCache[query.query];
+ // if we've got a cached dispatcher, just use that
+ if(retFunc){ return retFunc; }
+ // else, generate a new on
+
+ // NOTE:
+ // this function returns a function that searches for nodes and
+ // filters them. The search may be specialized by infix operators
+ // (">", "~", or "+") else it will default to searching all
+ // descendants (the " " selector). Once a group of children is
+ // found, a test function is applied to weed out the ones we
+ // don't want. Many common cases can be fast-pathed. We spend a
+ // lot of cycles to create a dispatcher that doesn't do more work
+ // than necessary at any point since, unlike this function, the
+ // dispatchers will be called every time. The logic of generating
+ // efficient dispatchers looks like this in pseudo code:
+ //
+ // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
+ // if infixOperator == " ":
+ // if only(id):
+ // return def(root):
+ // return d.byId(id, root);
+ //
+ // elif id:
+ // return def(root):
+ // return filter(d.byId(id, root));
+ //
+ // elif cssClass && getElementsByClassName:
+ // return def(root):
+ // return filter(root.getElementsByClassName(cssClass));
+ //
+ // elif only(tag):
+ // return def(root):
+ // return root.getElementsByTagName(tagName);
+ //
+ // else:
+ // # search by tag name, then filter
+ // return def(root):
+ // return filter(root.getElementsByTagName(tagName||"*"));
+ //
+ // elif infixOperator == ">":
+ // # search direct children
+ // return def(root):
+ // return filter(root.children);
+ //
+ // elif infixOperator == "+":
+ // # search next sibling
+ // return def(root):
+ // return filter(root.nextElementSibling);
+ //
+ // elif infixOperator == "~":
+ // # search rightward siblings
+ // return def(root):
+ // return filter(nextSiblings(root));
+
+ var io = query.infixOper;
+ var oper = (io ? io.oper : "");
+ // the default filter func which tests for all conditions in the query
+ // part. This is potentially inefficient, so some optimized paths may
+ // re-define it to test fewer things.
+ var filterFunc = getSimpleFilterFunc(query, { el: 1 });
+ var qt = query.tag;
+ var wildcardTag = ("*" == qt);
+ var ecs = getDoc()["getElementsByClassName"];
+
+ if(!oper){
+ // if there's no infix operator, then it's a descendant query. ID
+ // and "elements by class name" variants can be accelerated so we
+ // call them out explicitly:
+ if(query.id){
+ // testing shows that the overhead of yesman() is acceptable
+ // and can save us some bytes vs. re-defining the function
+ // everywhere.
+ filterFunc = (!query.loops && wildcardTag) ?
+ yesman :
+ getSimpleFilterFunc(query, { el: 1, id: 1 });
+
+ retFunc = function(root, arr){
+ var te = dom.byId(query.id, (root.ownerDocument||root));
+ if(!te || !filterFunc(te)){ return; }
+ if(9 == root.nodeType){ // if root's a doc, we just return directly
+ return getArr(te, arr);
+ }else{ // otherwise check ancestry
+ if(_isDescendant(te, root)){
+ return getArr(te, arr);
+ }
+ }
+ };
+ }else if(
+ ecs &&
+ // isAlien check. Workaround for Prototype.js being totally evil/dumb.
+ /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
+ query.classes.length &&
+ !cssCaseBug
+ ){
+ // it's a class-based query and we've got a fast way to run it.
+
+ // ignore class and ID filters since we will have handled both
+ filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
+ var classesString = query.classes.join(" ");
+ retFunc = function(root, arr, bag){
+ var ret = getArr(0, arr), te, x=0;
+ var tret = root.getElementsByClassName(classesString);
+ while((te = tret[x++])){
+ if(filterFunc(te, root) && _isUnique(te, bag)){
+ ret.push(te);
+ }
+ }
+ return ret;
+ };
+
+ }else if(!wildcardTag && !query.loops){
+ // it's tag only. Fast-path it.
+ retFunc = function(root, arr, bag){
+ var ret = getArr(0, arr), te, x=0;
+ var tag = query.getTag(),
+ tret = tag ? root.getElementsByTagName(tag) : [];
+ while((te = tret[x++])){
+ if(_isUnique(te, bag)){
+ ret.push(te);
+ }
+ }
+ return ret;
+ };
+ }else{
+ // the common case:
+ // a descendant selector without a fast path. By now it's got
+ // to have a tag selector, even if it's just "*" so we query
+ // by that and filter
+ filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
+ retFunc = function(root, arr, bag){
+ var ret = getArr(0, arr), te, x=0;
+ // we use getTag() to avoid case sensitivity issues
+ var tag = query.getTag(),
+ tret = tag ? root.getElementsByTagName(tag) : [];
+ while((te = tret[x++])){
+ if(filterFunc(te, root) && _isUnique(te, bag)){
+ ret.push(te);
+ }
+ }
+ return ret;
+ };
+ }
+ }else{
+ // the query is scoped in some way. Instead of querying by tag we
+ // use some other collection to find candidate nodes
+ var skipFilters = { el: 1 };
+ if(wildcardTag){
+ skipFilters.tag = 1;
+ }
+ filterFunc = getSimpleFilterFunc(query, skipFilters);
+ if("+" == oper){
+ retFunc = _nextSibling(filterFunc);
+ }else if("~" == oper){
+ retFunc = _nextSiblings(filterFunc);
+ }else if(">" == oper){
+ retFunc = _childElements(filterFunc);
+ }
+ }
+ // cache it and return
+ return _getElementsFuncCache[query.query] = retFunc;
+ };
+
+ var filterDown = function(root, queryParts){
+ // NOTE:
+ // this is the guts of the DOM query system. It takes a list of
+ // parsed query parts and a root and finds children which match
+ // the selector represented by the parts
+ var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
+
+ for(var i = 0; i < qpl; i++){
+ ret = [];
+ qp = queryParts[i];
+ x = candidates.length - 1;
+ if(x > 0){
+ // if we have more than one root at this level, provide a new
+ // hash to use for checking group membership but tell the
+ // system not to post-filter us since we will already have been
+ // guaranteed to be unique
+ bag = {};
+ ret.nozip = true;
+ }
+ var gef = getElementsFunc(qp);
+ for(var j = 0; (te = candidates[j]); j++){
+ // for every root, get the elements that match the descendant
+ // selector, adding them to the "ret" array and filtering them
+ // via membership in this level's bag. If there are more query
+ // parts, then this level's return will be used as the next
+ // level's candidates
+ gef(te, ret, bag);
+ }
+ if(!ret.length){ break; }
+ candidates = ret;
+ }
+ return ret;
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // the query runner
+ ////////////////////////////////////////////////////////////////////////
+
+ // these are the primary caches for full-query results. The query
+ // dispatcher functions are generated then stored here for hash lookup in
+ // the future
+ var _queryFuncCacheDOM = {},
+ _queryFuncCacheQSA = {};
+
+ // this is the second level of splitting, from full-length queries (e.g.,
+ // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
+ // ".bar"])
+ var getStepQueryFunc = function(query){
+ var qparts = getQueryParts(trim(query));
+
+ // if it's trivial, avoid iteration and zipping costs
+ if(qparts.length == 1){
+ // we optimize this case here to prevent dispatch further down the
+ // chain, potentially slowing things down. We could more elegantly
+ // handle this in filterDown(), but it's slower for simple things
+ // that need to be fast (e.g., "#someId").
+ var tef = getElementsFunc(qparts[0]);
+ return function(root){
+ var r = tef(root, []);
+ if(r){ r.nozip = true; }
+ return r;
+ };
+ }
+
+ // otherwise, break it up and return a runner that iterates over the parts recursively
+ return function(root){
+ return filterDown(root, qparts);
+ };
+ };
+
+ // NOTES:
+ // * we can't trust QSA for anything but document-rooted queries, so
+ // caching is split into DOM query evaluators and QSA query evaluators
+ // * caching query results is dirty and leak-prone (or, at a minimum,
+ // prone to unbounded growth). Other toolkits may go this route, but
+ // they totally destroy their own ability to manage their memory
+ // footprint. If we implement it, it should only ever be with a fixed
+ // total element reference # limit and an LRU-style algorithm since JS
+ // has no weakref support. Caching compiled query evaluators is also
+ // potentially problematic, but even on large documents the size of the
+ // query evaluators is often < 100 function objects per evaluator (and
+ // LRU can be applied if it's ever shown to be an issue).
+ // * since IE's QSA support is currently only for HTML documents and even
+ // then only in IE 8's "standards mode", we have to detect our dispatch
+ // route at query time and keep 2 separate caches. Ugg.
+
+ // we need to determine if we think we can run a given query via
+ // querySelectorAll or if we'll need to fall back on DOM queries to get
+ // there. We need a lot of information about the environment and the query
+ // to make the determination (e.g. does it support QSA, does the query in
+ // question work in the native QSA impl, etc.).
+
+ // IE QSA queries may incorrectly include comment nodes, so we throw the
+ // zipping function into "remove" comments mode instead of the normal "skip
+ // it" which every other QSA-clued browser enjoys
+ var noZip = has("ie") ? "commentStrip" : "nozip";
+
+ var qsa = "querySelectorAll";
+ var qsaAvail = !!getDoc()[qsa];
+
+ //Don't bother with n+3 type of matches, IE complains if we modify those.
+ var infixSpaceRe = /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
+ var infixSpaceFunc = function(match, pre, ch, post){
+ return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match;
+ };
+
+ //Don't apply the infixSpaceRe to attribute value selectors
+ var attRe = /([^[]*)([^\]]*])?/g;
+ var attFunc = function(match, nonAtt, att){
+ return nonAtt.replace(infixSpaceRe, infixSpaceFunc) + (att||"");
+ };
+ var getQueryFunc = function(query, forceDOM){
+ //Normalize query. The CSS3 selectors spec allows for omitting spaces around
+ //infix operators, >, ~ and +
+ //Do the work here since detection for spaces is used as a simple "not use QSA"
+ //test below.
+ query = query.replace(attRe, attFunc);
+
+ if(qsaAvail){
+ // if we've got a cached variant and we think we can do it, run it!
+ var qsaCached = _queryFuncCacheQSA[query];
+ if(qsaCached && !forceDOM){ return qsaCached; }
+ }
+
+ // else if we've got a DOM cached variant, assume that we already know
+ // all we need to and use it
+ var domCached = _queryFuncCacheDOM[query];
+ if(domCached){ return domCached; }
+
+ // TODO:
+ // today we're caching DOM and QSA branches separately so we
+ // recalc useQSA every time. If we had a way to tag root+query
+ // efficiently, we'd be in good shape to do a global cache.
+
+ var qcz = query.charAt(0);
+ var nospace = (-1 == query.indexOf(" "));
+
+ // byId searches are wicked fast compared to QSA, even when filtering
+ // is required
+ if( (query.indexOf("#") >= 0) && (nospace) ){
+ forceDOM = true;
+ }
+
+ var useQSA = (
+ qsaAvail && (!forceDOM) &&
+ // as per CSS 3, we can't currently start w/ combinator:
+ // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
+ (specials.indexOf(qcz) == -1) &&
+ // IE's QSA impl sucks on pseudos
+ (!has("ie") || (query.indexOf(":") == -1)) &&
+
+ (!(cssCaseBug && (query.indexOf(".") >= 0))) &&
+
+ // FIXME:
+ // need to tighten up browser rules on ":contains" and "|=" to
+ // figure out which aren't good
+ // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
+ // elements, even though according to spec, selected options should
+ // match :checked. So go nonQSA for it:
+ // http://bugs.dojotoolkit.org/ticket/5179
+ (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) &&
+ (query.indexOf("|=") == -1) // some browsers don't grok it
+ );
+
+ // TODO:
+ // if we've got a descendant query (e.g., "> .thinger" instead of
+ // just ".thinger") in a QSA-able doc, but are passed a child as a
+ // root, it should be possible to give the item a synthetic ID and
+ // trivially rewrite the query to the form "#synid > .thinger" to
+ // use the QSA branch
+
+
+ if(useQSA){
+ var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ?
+ (query + " *") : query;
+ return _queryFuncCacheQSA[query] = function(root){
+ try{
+ // the QSA system contains an egregious spec bug which
+ // limits us, effectively, to only running QSA queries over
+ // entire documents. See:
+ // http://ejohn.org/blog/thoughts-on-queryselectorall/
+ // despite this, we can also handle QSA runs on simple
+ // selectors, but we don't want detection to be expensive
+ // so we're just checking for the presence of a space char
+ // right now. Not elegant, but it's cheaper than running
+ // the query parser when we might not need to
+ if(!((9 == root.nodeType) || nospace)){ throw ""; }
+ var r = root[qsa](tq);
+ // skip expensive duplication checks and just wrap in a NodeList
+ r[noZip] = true;
+ return r;
+ }catch(e){
+ // else run the DOM branch on this query, ensuring that we
+ // default that way in the future
+ return getQueryFunc(query, true)(root);
+ }
+ };
+ }else{
+ // DOM branch
+ var parts = query.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
+ return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
+ // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
+ getStepQueryFunc(query) :
+ // if it *is* a complex query, break it up into its
+ // constituent parts and return a dispatcher that will
+ // merge the parts when run
+ function(root){
+ var pindex = 0, // avoid array alloc for every invocation
+ ret = [],
+ tp;
+ while((tp = parts[pindex++])){
+ ret = ret.concat(getStepQueryFunc(tp)(root));
+ }
+ return ret;
+ }
+ );
+ }
+ };
+
+ var _zipIdx = 0;
+
+ // NOTE:
+ // this function is Moo inspired, but our own impl to deal correctly
+ // with XML in IE
+ var _nodeUID = has("ie") ? function(node){
+ if(caseSensitive){
+ // XML docs don't have uniqueID on their nodes
+ return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
+
+ }else{
+ return node.uniqueID;
+ }
+ } :
+ function(node){
+ return (node._uid || (node._uid = ++_zipIdx));
+ };
+
+ // determine if a node in is unique in a "bag". In this case we don't want
+ // to flatten a list of unique items, but rather just tell if the item in
+ // question is already in the bag. Normally we'd just use hash lookup to do
+ // this for us but IE's DOM is busted so we can't really count on that. On
+ // the upside, it gives us a built in unique ID function.
+ var _isUnique = function(node, bag){
+ if(!bag){ return 1; }
+ var id = _nodeUID(node);
+ if(!bag[id]){ return bag[id] = 1; }
+ return 0;
+ };
+
+ // attempt to efficiently determine if an item in a list is a dupe,
+ // returning a list of "uniques", hopefully in document order
+ var _zipIdxName = "_zipIdx";
+ var _zip = function(arr){
+ if(arr && arr.nozip){ return arr; }
+
+ if(!arr || !arr.length){ return []; }
+ if(arr.length < 2){ return [arr[0]]; }
+
+ var ret = [];
+
+ _zipIdx++;
+
+ // we have to fork here for IE and XML docs because we can't set
+ // expandos on their nodes (apparently). *sigh*
+ var x, te;
+ if(has("ie") && caseSensitive){
+ var szidx = _zipIdx+"";
+ for(x = 0; x < arr.length; x++){
+ if((te = arr[x]) && te.getAttribute(_zipIdxName) != szidx){
+ ret.push(te);
+ te.setAttribute(_zipIdxName, szidx);
+ }
+ }
+ }else if(has("ie") && arr.commentStrip){
+ try{
+ for(x = 0; x < arr.length; x++){
+ if((te = arr[x]) && _isElement(te)){
+ ret.push(te);
+ }
+ }
+ }catch(e){ /* squelch */ }
+ }else{
+ for(x = 0; x < arr.length; x++){
+ if((te = arr[x]) && te[_zipIdxName] != _zipIdx){
+ ret.push(te);
+ te[_zipIdxName] = _zipIdx;
+ }
+ }
+ }
+ return ret;
+ };
+
+ // the main executor
+ var query = function(/*String*/ query, /*String|DOMNode?*/ root){
+ // summary:
+ // Returns nodes which match the given CSS3 selector, searching the
+ // entire document by default but optionally taking a node to scope
+ // the search by. Returns an array.
+ // description:
+ // dojo.query() is the swiss army knife of DOM node manipulation in
+ // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
+ // "$" function, dojo.query provides robust, high-performance
+ // CSS-based node selector support with the option of scoping searches
+ // to a particular sub-tree of a document.
+ //
+ // Supported Selectors:
+ // --------------------
+ //
+ // acme supports a rich set of CSS3 selectors, including:
+ //
+ // - class selectors (e.g., `.foo`)
+ // - node type selectors like `span`
+ // - ` ` descendant selectors
+ // - `>` child element selectors
+ // - `#foo` style ID selectors
+ // - `*` universal selector
+ // - `~`, the preceded-by sibling selector
+ // - `+`, the immediately preceded-by sibling selector
+ // - attribute queries:
+ // - `[foo]` attribute presence selector
+ // - `[foo='bar']` attribute value exact match
+ // - `[foo~='bar']` attribute value list item match
+ // - `[foo^='bar']` attribute start match
+ // - `[foo$='bar']` attribute end match
+ // - `[foo*='bar']` attribute substring match
+ // - `:first-child`, `:last-child`, and `:only-child` positional selectors
+ // - `:empty` content emtpy selector
+ // - `:checked` pseudo selector
+ // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
+ // - `:nth-child(even)`, `:nth-child(odd)` positional selectors
+ // - `:not(...)` negation pseudo selectors
+ //
+ // Any legal combination of these selectors will work with
+ // `dojo.query()`, including compound selectors ("," delimited).
+ // Very complex and useful searches can be constructed with this
+ // palette of selectors and when combined with functions for
+ // manipulation presented by dojo/NodeList, many types of DOM
+ // manipulation operations become very straightforward.
+ //
+ // Unsupported Selectors:
+ // ----------------------
+ //
+ // While dojo.query handles many CSS3 selectors, some fall outside of
+ // what's reasonable for a programmatic node querying engine to
+ // handle. Currently unsupported selectors include:
+ //
+ // - namespace-differentiated selectors of any form
+ // - all `::` pseduo-element selectors
+ // - certain pseudo-selectors which don't get a lot of day-to-day use:
+ // - `:root`, `:lang()`, `:target`, `:focus`
+ // - all visual and state selectors:
+ // - `:root`, `:active`, `:hover`, `:visited`, `:link`,
+ // `:enabled`, `:disabled`
+ // - `:*-of-type` pseudo selectors
+ //
+ // dojo.query and XML Documents:
+ // -----------------------------
+ //
+ // `dojo.query` (as of dojo 1.2) supports searching XML documents
+ // in a case-sensitive manner. If an HTML document is served with
+ // a doctype that forces case-sensitivity (e.g., XHTML 1.1
+ // Strict), dojo.query() will detect this and "do the right
+ // thing". Case sensitivity is dependent upon the document being
+ // searched and not the query used. It is therefore possible to
+ // use case-sensitive queries on strict sub-documents (iframes,
+ // etc.) or XML documents while still assuming case-insensitivity
+ // for a host/root document.
+ //
+ // Non-selector Queries:
+ // ---------------------
+ //
+ // If something other than a String is passed for the query,
+ // `dojo.query` will return a new `dojo/NodeList` instance
+ // constructed from that parameter alone and all further
+ // processing will stop. This means that if you have a reference
+ // to a node or NodeList, you can quickly construct a new NodeList
+ // from the original by calling `dojo.query(node)` or
+ // `dojo.query(list)`.
+ //
+ // query:
+ // The CSS3 expression to match against. For details on the syntax of
+ // CSS3 selectors, see
+ // root:
+ // A DOMNode (or node id) to scope the search from. Optional.
+ // returns: Array
+ // example:
+ // search the entire document for elements with the class "foo":
+ // | dojo.query(".foo");
+ // these elements will match:
+ // |
+ // |
+ // |
+ // example:
+ // search the entire document for elements with the classes "foo" *and* "bar":
+ // | dojo.query(".foo.bar");
+ // these elements will match:
+ // |
+ // while these will not:
+ // |
+ // |
+ // example:
+ // find `` elements which are descendants of paragraphs and
+ // which have a "highlighted" class:
+ // | dojo.query("p span.highlighted");
+ // the innermost span in this fragment matches:
+ // |
+ // | ...
+ // | ...
+ // |
+ // |
+ // example:
+ // set an "odd" class on all odd table rows inside of the table
+ // `#tabular_data`, using the `>` (direct child) selector to avoid
+ // affecting any nested tables:
+ // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
+ // example:
+ // remove all elements with the class "error" from the document
+ // and store them in a list:
+ // | var errors = dojo.query(".error").orphan();
+ // example:
+ // add an onclick handler to every submit button in the document
+ // which causes the form to be sent via Ajax instead:
+ // | dojo.query("input[type='submit']").onclick(function(e){
+ // | dojo.stopEvent(e); // prevent sending the form
+ // | var btn = e.target;
+ // | dojo.xhrPost({
+ // | form: btn.form,
+ // | load: function(data){
+ // | // replace the form with the response
+ // | var div = dojo.doc.createElement("div");
+ // | dojo.place(div, btn.form, "after");
+ // | div.innerHTML = data;
+ // | dojo.style(btn.form, "display", "none");
+ // | }
+ // | });
+ // | });
+
+ root = root || getDoc();
+
+ // throw the big case sensitivity switch
+ var od = root.ownerDocument || root; // root is either Document or a node inside the document
+ caseSensitive = (od.createElement("div").tagName === "div");
+
+ // NOTE:
+ // adding "true" as the 2nd argument to getQueryFunc is useful for
+ // testing the DOM branch without worrying about the
+ // behavior/performance of the QSA branch.
+ var r = getQueryFunc(query)(root);
+
+ // FIXME:
+ // need to investigate this branch WRT #8074 and #8075
+ if(r && r.nozip){
+ return r;
+ }
+ return _zip(r); // dojo/NodeList
+ };
+ query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
+ // summary:
+ // function for filtering a NodeList based on a selector, optimized for simple selectors
+ var tmpNodeList = [],
+ parts = getQueryParts(filter),
+ filterFunc =
+ (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
+ getSimpleFilterFunc(parts[0]) :
+ function(node){
+ return array.indexOf(query(filter, dom.byId(root)), node) != -1;
+ };
+ for(var x = 0, te; te = nodeList[x]; x++){
+ if(filterFunc(te)){ tmpNodeList.push(te); }
+ }
+ return tmpNodeList;
+ };
+ return query;
+});
diff --git a/src/main/resources/static/dojo/selector/lite.js b/src/main/resources/static/dojo/selector/lite.js
new file mode 100644
index 0000000000000000000000000000000000000000..50f1ed5e39f5257e869efdbe30c61a00ee55129f
--- /dev/null
+++ b/src/main/resources/static/dojo/selector/lite.js
@@ -0,0 +1,283 @@
+define(["../has", "../_base/kernel"], function(has, dojo){
+"use strict";
+
+var testDiv = document.createElement("div");
+var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; // IE9, WebKit, Firefox have this, but not Opera yet
+var querySelectorAll = testDiv.querySelectorAll;
+var unionSplit = /([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g;
+has.add("dom-matches-selector", !!matchesSelector);
+has.add("dom-qsa", !!querySelectorAll);
+
+// this is a simple query engine. It has handles basic selectors, and for simple
+// common selectors is extremely fast
+var liteEngine = function(selector, root){
+ // summary:
+ // A small lightweight query selector engine that implements CSS2.1 selectors
+ // minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
+
+ if(combine && selector.indexOf(',') > -1){
+ return combine(selector, root);
+ }
+ // use the root's ownerDocument if provided, otherwise try to use dojo.doc. Note
+ // that we don't use dojo/_base/window's doc to reduce dependencies, and
+ // fallback to plain document if dojo.doc hasn't been defined (by dojo/_base/window).
+ // presumably we will have a better way to do this in 2.0
+ var doc = root ? root.ownerDocument || root : dojo.doc || document,
+ match = (querySelectorAll ?
+ /^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // this one only matches on simple queries where we can beat qSA with specific methods
+ /^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
+ .exec(selector);
+ root = root || doc;
+ if(match){
+ // fast path regardless of whether or not querySelectorAll exists
+ if(match[2]){
+ // an #id
+ // use dojo.byId if available as it fixes the id retrieval in IE, note that we can't use the dojo namespace in 2.0, but if there is a conditional module use, we will use that
+ var found = dojo.byId ? dojo.byId(match[2], doc) : doc.getElementById(match[2]);
+ if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
+ // if there is a tag qualifer and it doesn't match, no matches
+ return [];
+ }
+ if(root != doc){
+ // there is a root element, make sure we are a child of it
+ var parent = found;
+ while(parent != root){
+ parent = parent.parentNode;
+ if(!parent){
+ return [];
+ }
+ }
+ }
+ return match[3] ?
+ liteEngine(match[3], found)
+ : [found];
+ }
+ if(match[3] && root.getElementsByClassName){
+ // a .class
+ return root.getElementsByClassName(match[4]);
+ }
+ var found;
+ if(match[5]){
+ // a tag
+ found = root.getElementsByTagName(match[5]);
+ if(match[4] || match[6]){
+ selector = (match[4] || "") + match[6];
+ }else{
+ // that was the entirety of the query, return results
+ return found;
+ }
+ }
+ }
+ if(querySelectorAll){
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){
+ return useRoot(root, selector, root.querySelectorAll);
+ }else{
+ // we can use the native qSA
+ return root.querySelectorAll(selector);
+ }
+ }else if(!found){
+ // search all children and then filter
+ found = root.getElementsByTagName("*");
+ }
+ // now we filter the nodes that were found using the matchesSelector
+ var results = [];
+ for(var i = 0, l = found.length; i < l; i++){
+ var node = found[i];
+ if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){
+ // keep the nodes that match the selector
+ results.push(node);
+ }
+ }
+ return results;
+};
+var useRoot = function(context, query, method){
+ // this function creates a temporary id so we can do rooted qSA queries, this is taken from sizzle
+ var oldContext = context,
+ old = context.getAttribute("id"),
+ nid = old || "__dojo__",
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test(query);
+
+ if(relativeHierarchySelector && !hasParent){
+ return [];
+ }
+ if(!old){
+ context.setAttribute("id", nid);
+ }else{
+ nid = nid.replace(/'/g, "\\$&");
+ }
+ if(relativeHierarchySelector && hasParent){
+ context = context.parentNode;
+ }
+ var selectors = query.match(unionSplit);
+ for(var i = 0; i < selectors.length; i++){
+ selectors[i] = "[id='" + nid + "'] " + selectors[i];
+ }
+ query = selectors.join(",");
+
+ try{
+ return method.call(context, query);
+ }finally{
+ if(!old){
+ oldContext.removeAttribute("id");
+ }
+ }
+};
+
+if(!has("dom-matches-selector")){
+ var jsMatchesSelector = (function(){
+ // a JS implementation of CSS selector matching, first we start with the various handlers
+ var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";
+ var selectorTypes = {
+ "": function(tagName){
+ tagName = tagName[caseFix]();
+ return function(node){
+ return node.tagName == tagName;
+ };
+ },
+ ".": function(className){
+ var classNameSpaced = ' ' + className + ' ';
+ return function(node){
+ return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
+ };
+ },
+ "#": function(id){
+ return function(node){
+ return node.id == id;
+ };
+ }
+ };
+ var attrComparators = {
+ "^=": function(attrValue, value){
+ return attrValue.indexOf(value) == 0;
+ },
+ "*=": function(attrValue, value){
+ return attrValue.indexOf(value) > -1;
+ },
+ "$=": function(attrValue, value){
+ return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
+ },
+ "~=": function(attrValue, value){
+ return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
+ },
+ "|=": function(attrValue, value){
+ return (attrValue + '-').indexOf(value + '-') == 0;
+ },
+ "=": function(attrValue, value){
+ return attrValue == value;
+ },
+ "": function(attrValue, value){
+ return true;
+ }
+ };
+ function attr(name, value, type){
+ var firstChar = value.charAt(0);
+ if(firstChar == '"' || firstChar == "'"){
+ // it is quoted, remove the quotes
+ value = value.slice(1, -1);
+ }
+ value = value.replace(/\\/g,'');
+ var comparator = attrComparators[type || ""];
+ return function(node){
+ var attrValue = node.getAttribute(name);
+ return attrValue && comparator(attrValue, value);
+ };
+ }
+ function ancestor(matcher){
+ return function(node, root){
+ while((node = node.parentNode) != root){
+ if(matcher(node, root)){
+ return true;
+ }
+ }
+ };
+ }
+ function parent(matcher){
+ return function(node, root){
+ node = node.parentNode;
+ return matcher ?
+ node != root && matcher(node, root)
+ : node == root;
+ };
+ }
+ var cache = {};
+ function and(matcher, next){
+ return matcher ?
+ function(node, root){
+ return next(node) && matcher(node, root);
+ }
+ : next;
+ }
+ return function(node, selector, root){
+ // this returns true or false based on if the node matches the selector (optionally within the given root)
+ var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
+ if(!matcher){
+ // create a matcher function for the given selector
+ // parse the selectors
+ if(selector.replace(/(?:\s*([> ])\s*)|(#|\.)?((?:\\.|[\w-])+)|\[\s*([\w-]+)\s*(.?=)?\s*("(?:\\.|[^"])+"|'(?:\\.|[^'])+'|(?:\\.|[^\]])*)\s*\]/g, function(t, combinator, type, value, attrName, attrType, attrValue){
+ if(value){
+ matcher = and(matcher, selectorTypes[type || ""](value.replace(/\\/g, '')));
+ }
+ else if(combinator){
+ matcher = (combinator == " " ? ancestor : parent)(matcher);
+ }
+ else if(attrName){
+ matcher = and(matcher, attr(attrName, attrValue, attrType));
+ }
+ return "";
+ })){
+ throw new Error("Syntax error in query");
+ }
+ if(!matcher){
+ return true;
+ }
+ cache[selector] = matcher;
+ }
+ // now run the matcher function on the node
+ return matcher(node, root);
+ };
+ })();
+}
+if(!has("dom-qsa")){
+ var combine = function(selector, root){
+ // combined queries
+ var selectors = selector.match(unionSplit);
+ var indexed = [];
+ // add all results and keep unique ones, this only runs in IE, so we take advantage
+ // of known IE features, particularly sourceIndex which is unique and allows us to
+ // order the results
+ for(var i = 0; i < selectors.length; i++){
+ selector = new String(selectors[i].replace(/\s*$/,''));
+ selector.indexOf = escape; // keep it from recursively entering combine
+ var results = liteEngine(selector, root);
+ for(var j = 0, l = results.length; j < l; j++){
+ var node = results[j];
+ indexed[node.sourceIndex] = node;
+ }
+ }
+ // now convert from a sparse array to a dense array
+ var totalResults = [];
+ for(i in indexed){
+ totalResults.push(indexed[i]);
+ }
+ return totalResults;
+ };
+}
+
+liteEngine.match = matchesSelector ? function(node, selector, root){
+ if(root && root.nodeType != 9){
+ // doesn't support three args, use rooted id trick
+ return useRoot(root, selector, function(query){
+ return matchesSelector.call(node, query);
+ });
+ }
+ // we have a native matchesSelector, use that
+ return matchesSelector.call(node, selector);
+} : jsMatchesSelector; // otherwise use the JS matches impl
+
+return liteEngine;
+});
diff --git a/src/main/resources/static/dojo/sniff.js b/src/main/resources/static/dojo/sniff.js
new file mode 100644
index 0000000000000000000000000000000000000000..99266d5e0dd7355821ef622a7fdea850bfc4b8fc
--- /dev/null
+++ b/src/main/resources/static/dojo/sniff.js
@@ -0,0 +1,80 @@
+define(["./has"], function(has){
+ // module:
+ // dojo/sniff
+
+ /*=====
+ return function(){
+ // summary:
+ // This module sets has() flags based on the current browser.
+ // It returns the has() function.
+ };
+ =====*/
+
+ if(has("host-browser")){
+ var n = navigator,
+ dua = n.userAgent,
+ dav = n.appVersion,
+ tv = parseFloat(dav);
+
+ has.add("air", dua.indexOf("AdobeAIR") >= 0);
+ has.add("msapp", parseFloat(dua.split("MSAppHost/")[1]) || undefined);
+ has.add("khtml", dav.indexOf("Konqueror") >= 0 ? tv : undefined);
+ has.add("webkit", parseFloat(dua.split("WebKit/")[1]) || undefined);
+ has.add("chrome", parseFloat(dua.split("Chrome/")[1]) || undefined);
+ has.add("safari", dav.indexOf("Safari")>=0 && !has("chrome") ? parseFloat(dav.split("Version/")[1]) : undefined);
+ has.add("mac", dav.indexOf("Macintosh") >= 0);
+ has.add("quirks", document.compatMode == "BackCompat");
+ if(dua.match(/(iPhone|iPod|iPad)/)){
+ var p = RegExp.$1.replace(/P/, "p");
+ var v = dua.match(/OS ([\d_]+)/) ? RegExp.$1 : "1";
+ var os = parseFloat(v.replace(/_/, ".").replace(/_/g, ""));
+ has.add(p, os); // "iphone", "ipad" or "ipod"
+ has.add("ios", os);
+ }
+ has.add("android", parseFloat(dua.split("Android ")[1]) || undefined);
+ has.add("bb", (dua.indexOf("BlackBerry") >= 0 || dua.indexOf("BB10") >= 0) && parseFloat(dua.split("Version/")[1]) || undefined);
+
+ has.add("svg", typeof SVGAngle !== "undefined");
+
+ if(!has("webkit")){
+ // Opera
+ if(dua.indexOf("Opera") >= 0){
+ // see http://dev.opera.com/articles/view/opera-ua-string-changes and http://www.useragentstring.com/pages/Opera/
+ // 9.8 has both styles; <9.8, 9.9 only old style
+ has.add("opera", tv >= 9.8 ? parseFloat(dua.split("Version/")[1]) || tv : tv);
+ }
+
+ // Mozilla and firefox
+ if(dua.indexOf("Gecko") >= 0 && !has("khtml") && !has("webkit")){
+ has.add("mozilla", tv);
+ }
+ if(has("mozilla")){
+ //We really need to get away from this. Consider a sane isGecko approach for the future.
+ has.add("ff", parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1]) || undefined);
+ }
+
+ // IE
+ if(document.all && !has("opera")){
+ var isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
+
+ //In cases where the page has an HTTP header or META tag with
+ //X-UA-Compatible, then it is in emulation mode.
+ //Make sure isIE reflects the desired version.
+ //document.documentMode of 5 means quirks mode.
+ //Only switch the value if documentMode's major version
+ //is different from isIE's major version.
+ var mode = document.documentMode;
+ if(mode && mode != 5 && Math.floor(isIE) != mode){
+ isIE = mode;
+ }
+
+ has.add("ie", isIE);
+ }
+
+ // Wii
+ has.add("wii", typeof opera != "undefined" && opera.wiiremote);
+ }
+ }
+
+ return has;
+});
diff --git a/src/main/resources/static/dojo/store/Cache.js b/src/main/resources/static/dojo/store/Cache.js
new file mode 100644
index 0000000000000000000000000000000000000000..66de169b15e09c1961dd1506a7f87606b53d3178
--- /dev/null
+++ b/src/main/resources/static/dojo/store/Cache.js
@@ -0,0 +1,146 @@
+define(["../_base/lang","../when" /*=====, "../_base/declare", "./api/Store" =====*/],
+function(lang, when /*=====, declare, Store =====*/){
+
+// module:
+// dojo/store/Cache
+
+var Cache = function(masterStore, cachingStore, options){
+ options = options || {};
+ return lang.delegate(masterStore, {
+ query: function(query, directives){
+ var results = masterStore.query(query, directives);
+ results.forEach(function(object){
+ if(!options.isLoaded || options.isLoaded(object)){
+ cachingStore.put(object);
+ }
+ });
+ return results;
+ },
+ // look for a queryEngine in either store
+ queryEngine: masterStore.queryEngine || cachingStore.queryEngine,
+ get: function(id, directives){
+ return when(cachingStore.get(id), function(result){
+ return result || when(masterStore.get(id, directives), function(result){
+ if(result){
+ cachingStore.put(result, {id: id});
+ }
+ return result;
+ });
+ });
+ },
+ add: function(object, directives){
+ return when(masterStore.add(object, directives), function(result){
+ // now put result in cache
+ cachingStore.add(object && typeof result == "object" ? result : object, directives);
+ return result; // the result from the add should be dictated by the masterStore and be unaffected by the cachingStore
+ });
+ },
+ put: function(object, directives){
+ // first remove from the cache, so it is empty until we get a response from the master store
+ cachingStore.remove((directives && directives.id) || this.getIdentity(object));
+ return when(masterStore.put(object, directives), function(result){
+ // now put result in cache
+ cachingStore.put(object && typeof result == "object" ? result : object, directives);
+ return result; // the result from the put should be dictated by the masterStore and be unaffected by the cachingStore
+ });
+ },
+ remove: function(id, directives){
+ return when(masterStore.remove(id, directives), function(result){
+ return cachingStore.remove(id, directives);
+ });
+ },
+ evict: function(id){
+ return cachingStore.remove(id);
+ }
+ });
+};
+lang.setObject("dojo.store.Cache", Cache);
+
+/*=====
+var __CacheArgs = {
+ // summary:
+ // These are additional options for how caching is handled.
+ // isLoaded: Function?
+ // This is a function that will be called for each item in a query response to determine
+ // if it is cacheable. If isLoaded returns true, the item will be cached, otherwise it
+ // will not be cached. If isLoaded is not provided, all items will be cached.
+};
+
+Cache = declare(Store, {
+ // summary:
+ // The Cache store wrapper takes a master store and a caching store,
+ // caches data from the master into the caching store for faster
+ // lookup. Normally one would use a memory store for the caching
+ // store and a server store like JsonRest for the master store.
+ // example:
+ // | var master = new Memory(data);
+ // | var cacher = new Memory();
+ // | var store = new Cache(master, cacher);
+ //
+ constructor: function(masterStore, cachingStore, options){
+ // masterStore:
+ // This is the authoritative store, all uncached requests or non-safe requests will
+ // be made against this store.
+ // cachingStore:
+ // This is the caching store that will be used to store responses for quick access.
+ // Typically this should be a local store.
+ // options: __CacheArgs?
+ // These are additional options for how caching is handled.
+ },
+ query: function(query, directives){
+ // summary:
+ // Query the underlying master store and cache any results.
+ // query: Object|String
+ // The object or string containing query information. Dependent on the query engine used.
+ // directives: dojo/store/api/Store.QueryOptions?
+ // An optional keyword arguments object with additional parameters describing the query.
+ // returns: dojo/store/api/Store.QueryResults
+ // A QueryResults object that can be used to iterate over.
+ },
+ get: function(id, directives){
+ // summary:
+ // Get the object with the specific id.
+ // id: Number
+ // The identifier for the object in question.
+ // directives: Object?
+ // Any additional parameters needed to describe how the get should be performed.
+ // returns: dojo/store/api/Store.QueryResults
+ // A QueryResults object.
+ },
+ add: function(object, directives){
+ // summary:
+ // Add the given object to the store.
+ // object: Object
+ // The object to add to the store.
+ // directives: dojo/store/api/Store.AddOptions?
+ // Any additional parameters needed to describe how the add should be performed.
+ // returns: Number
+ // The new id for the object.
+ },
+ put: function(object, directives){
+ // summary:
+ // Put the object into the store (similar to an HTTP PUT).
+ // object: Object
+ // The object to put to the store.
+ // directives: dojo/store/api/Store.PutDirectives?
+ // Any additional parameters needed to describe how the put should be performed.
+ // returns: Number
+ // The new id for the object.
+ },
+ remove: function(id){
+ // summary:
+ // Remove the object with the specific id.
+ // id: Number
+ // The identifier for the object in question.
+ },
+ evict: function(id){
+ // summary:
+ // Remove the object with the given id from the underlying caching store.
+ // id: Number
+ // The identifier for the object in question.
+ }
+});
+=====*/
+
+return Cache;
+});
diff --git a/src/main/resources/static/dojo/store/DataStore.js b/src/main/resources/static/dojo/store/DataStore.js
new file mode 100644
index 0000000000000000000000000000000000000000..c16a8d86c69a5f545827d3364ac16ea15d3eaef6
--- /dev/null
+++ b/src/main/resources/static/dojo/store/DataStore.js
@@ -0,0 +1,202 @@
+define([
+ "../_base/lang", "../_base/declare", "../Deferred", "../_base/array",
+ "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/
+], function(lang, declare, Deferred, array, QueryResults, SimpleQueryEngine /*=====, Store =====*/){
+
+// module:
+// dojo/store/DataStore
+
+
+// No base class, but for purposes of documentation, the base class is dojo/store/api/Store
+var base = null;
+/*===== base = Store; =====*/
+
+return declare("dojo.store.DataStore", base, {
+ // summary:
+ // This is an adapter for using Dojo Data stores with an object store consumer.
+ // You can provide a Dojo data store and use this adapter to interact with it through
+ // the Dojo object store API
+
+ target: "",
+ constructor: function(options){
+ // options: Object?
+ // This provides any configuration information that will be mixed into the store,
+ // including a reference to the Dojo data store under the property "store".
+ lang.mixin(this, options);
+ if(!"idProperty" in options){
+ var idAttribute;
+ try{
+ idAttribute = this.store.getIdentityAttributes();
+ }catch(e){
+ // some store are not requiring an item instance to give us the ID attributes
+ // but some other do and throw errors in that case.
+ }
+ // if no idAttribute we have implicit id
+ this.idProperty = (!idAttribute || !idAttributes[0]) || this.idProperty;
+ }
+ var features = this.store.getFeatures();
+ // check the feature set and null out any methods that shouldn't be available
+ if(!features["dojo.data.api.Read"]){
+ this.get = null;
+ }
+ if(!features["dojo.data.api.Identity"]){
+ this.getIdentity = null;
+ }
+ if(!features["dojo.data.api.Write"]){
+ this.put = this.add = null;
+ }
+ },
+ // idProperty: String
+ // The object property to use to store the identity of the store items.
+ idProperty: "id",
+ // store:
+ // The object store to convert to a data store
+ store: null,
+ // queryEngine: Function
+ // Defines the query engine to use for querying the data store
+ queryEngine: SimpleQueryEngine,
+
+ _objectConverter: function(callback){
+ var store = this.store;
+ var idProperty = this.idProperty;
+ function convert(item){
+ var object = {};
+ var attributes = store.getAttributes(item);
+ for(var i = 0; i < attributes.length; i++){
+ var attribute = attributes[i];
+ var values = store.getValues(item, attribute);
+ if(values.length > 1){
+ for(var j = 0; j < values.length; j++){
+ var value = values[j];
+ if(typeof value == 'object' && store.isItem(value)){
+ values[j] = convert(value);
+ }
+ }
+ value = values;
+ }else{
+ var value = store.getValue(item, attribute);
+ if(typeof value == 'object' && store.isItem(value)){
+ value = convert(value);
+ }
+ }
+ object[attributes[i]] = value;
+ }
+ if(!(idProperty in object) && store.getIdentity){
+ object[idProperty] = store.getIdentity(item);
+ }
+ return object;
+ }
+ return function(item){
+ return callback(convert(item));
+ };
+ },
+ get: function(id, options){
+ // summary:
+ // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity
+ // id: Object?
+ // The identity to use to lookup the object
+ var returnedObject, returnedError;
+ var deferred = new Deferred();
+ this.store.fetchItemByIdentity({
+ identity: id,
+ onItem: this._objectConverter(function(object){
+ deferred.resolve(returnedObject = object);
+ }),
+ onError: function(error){
+ deferred.reject(returnedError = error);
+ }
+ });
+ if(returnedObject){
+ // if it was returned synchronously
+ return returnedObject;
+ }
+ if(returnedError){
+ throw returnedError;
+ }
+ return deferred.promise;
+ },
+ put: function(object, options){
+ // summary:
+ // Stores an object by its identity.
+ // object: Object
+ // The object to store.
+ // options: Object?
+ // Additional metadata for storing the data. Includes a reference to an id
+ // that the object may be stored with (i.e. { id: "foo" }).
+ var id = options && typeof options.id != "undefined" || this.getIdentity(object);
+ var store = this.store;
+ var idProperty = this.idProperty;
+ if(typeof id == "undefined"){
+ store.newItem(object);
+ store.save();
+ }else{
+ store.fetchItemByIdentity({
+ identity: id,
+ onItem: function(item){
+ if(item){
+ for(var i in object){
+ if(i != idProperty && // don't copy id properties since they are immutable and should be omitted for implicit ids
+ store.getValue(item, i) != object[i]){
+ store.setValue(item, i, object[i]);
+ }
+ }
+ }else{
+ store.newItem(object);
+ }
+ store.save();
+ }
+ });
+ }
+ },
+ remove: function(id){
+ // summary:
+ // Deletes an object by its identity.
+ // id: Object
+ // The identity to use to delete the object
+ var store = this.store;
+ this.store.fetchItemByIdentity({
+ identity: id,
+ onItem: function(item){
+ store.deleteItem(item);
+ store.save();
+ }
+ });
+ },
+ query: function(query, options){
+ // summary:
+ // Queries the store for objects.
+ // query: Object
+ // The query to use for retrieving objects from the store
+ // options: Object?
+ // Optional options object as used by the underlying dojo.data Store.
+ // returns: dojo/store/api/Store.QueryResults
+ // A query results object that can be used to iterate over results.
+ var fetchHandle;
+ var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
+ deferred.total = new Deferred();
+ var converter = this._objectConverter(function(object){return object;});
+ fetchHandle = this.store.fetch(lang.mixin({
+ query: query,
+ onBegin: function(count){
+ deferred.total.resolve(count);
+ },
+ onComplete: function(results){
+ deferred.resolve(array.map(results, converter));
+ },
+ onError: function(error){
+ deferred.reject(error);
+ }
+ }, options));
+ return QueryResults(deferred);
+ },
+ getIdentity: function(object){
+ // summary:
+ // Fetch the identity for the given object.
+ // object: Object
+ // The data object to get the identity from.
+ // returns: Number
+ // The id of the given object.
+ return object[this.idProperty];
+ }
+});
+});
diff --git a/src/main/resources/static/dojo/store/JsonRest.js b/src/main/resources/static/dojo/store/JsonRest.js
new file mode 100644
index 0000000000000000000000000000000000000000..54d1c23d080732e8a329ec425a6c65fc51068122
--- /dev/null
+++ b/src/main/resources/static/dojo/store/JsonRest.js
@@ -0,0 +1,198 @@
+define(["../_base/xhr", "../_base/lang", "../json", "../_base/declare", "./util/QueryResults" /*=====, "./api/Store" =====*/
+], function(xhr, lang, JSON, declare, QueryResults /*=====, Store =====*/){
+
+// No base class, but for purposes of documentation, the base class is dojo/store/api/Store
+var base = null;
+/*===== base = Store; =====*/
+
+/*=====
+var __HeaderOptions = {
+ // headers: Object?
+ // Additional headers to send along with the request.
+ },
+ __PutDirectives = declare(Store.PutDirectives, __HeaderOptions),
+ __QueryOptions = declare(Store.QueryOptions, __HeaderOptions);
+=====*/
+
+return declare("dojo.store.JsonRest", base, {
+ // summary:
+ // This is a basic store for RESTful communicating with a server through JSON
+ // formatted data. It implements dojo/store/api/Store.
+
+ constructor: function(options){
+ // summary:
+ // This is a basic store for RESTful communicating with a server through JSON
+ // formatted data.
+ // options: dojo/store/JsonRest
+ // This provides any configuration information that will be mixed into the store
+ this.headers = {};
+ declare.safeMixin(this, options);
+ },
+
+ // headers: Object
+ // Additional headers to pass in all requests to the server. These can be overridden
+ // by passing additional headers to calls to the store.
+ headers: {},
+
+ // target: String
+ // The target base URL to use for all requests to the server. This string will be
+ // prepended to the id to generate the URL (relative or absolute) for requests
+ // sent to the server
+ target: "",
+
+ // idProperty: String
+ // Indicates the property to use as the identity property. The values of this
+ // property should be unique.
+ idProperty: "id",
+
+ // sortParam: String
+ // The query parameter to used for holding sort information. If this is omitted, than
+ // the sort information is included in a functional query token to avoid colliding
+ // with the set of name/value pairs.
+
+ // ascendingPrefix: String
+ // The prefix to apply to sort attribute names that are ascending
+ ascendingPrefix: "+",
+
+ // descendingPrefix: String
+ // The prefix to apply to sort attribute names that are ascending
+ descendingPrefix: "-",
+
+
+ get: function(id, options){
+ // summary:
+ // Retrieves an object by its identity. This will trigger a GET request to the server using
+ // the url `this.target + id`.
+ // id: Number
+ // The identity to use to lookup the object
+ // options: Object?
+ // HTTP headers. For consistency with other methods, if a `headers` key exists on this object, it will be
+ // used to provide HTTP headers instead.
+ // returns: Object
+ // The object in the store that matches the given id.
+ options = options || {};
+ var headers = lang.mixin({ Accept: this.accepts }, this.headers, options.headers || options);
+ return xhr("GET", {
+ url: this.target + id,
+ handleAs: "json",
+ headers: headers
+ });
+ },
+
+ // accepts: String
+ // Defines the Accept header to use on HTTP requests
+ accepts: "application/javascript, application/json",
+
+ getIdentity: function(object){
+ // summary:
+ // Returns an object's identity
+ // object: Object
+ // The object to get the identity from
+ // returns: Number
+ return object[this.idProperty];
+ },
+
+ put: function(object, options){
+ // summary:
+ // Stores an object. This will trigger a PUT request to the server
+ // if the object has an id, otherwise it will trigger a POST request.
+ // object: Object
+ // The object to store.
+ // options: __PutDirectives?
+ // Additional metadata for storing the data. Includes an "id"
+ // property if a specific id is to be used.
+ // returns: dojo/_base/Deferred
+ options = options || {};
+ var id = ("id" in options) ? options.id : this.getIdentity(object);
+ var hasId = typeof id != "undefined";
+ return xhr(hasId && !options.incremental ? "PUT" : "POST", {
+ url: hasId ? this.target + id : this.target,
+ postData: JSON.stringify(object),
+ handleAs: "json",
+ headers: lang.mixin({
+ "Content-Type": "application/json",
+ Accept: this.accepts,
+ "If-Match": options.overwrite === true ? "*" : null,
+ "If-None-Match": options.overwrite === false ? "*" : null
+ }, this.headers, options.headers)
+ });
+ },
+
+ add: function(object, options){
+ // summary:
+ // Adds an object. This will trigger a PUT request to the server
+ // if the object has an id, otherwise it will trigger a POST request.
+ // object: Object
+ // The object to store.
+ // options: __PutDirectives?
+ // Additional metadata for storing the data. Includes an "id"
+ // property if a specific id is to be used.
+ options = options || {};
+ options.overwrite = false;
+ return this.put(object, options);
+ },
+
+ remove: function(id, options){
+ // summary:
+ // Deletes an object by its identity. This will trigger a DELETE request to the server.
+ // id: Number
+ // The identity to use to delete the object
+ // options: __HeaderOptions?
+ // HTTP headers.
+ options = options || {};
+ return xhr("DELETE", {
+ url: this.target + id,
+ headers: lang.mixin({}, this.headers, options.headers)
+ });
+ },
+
+ query: function(query, options){
+ // summary:
+ // Queries the store for objects. This will trigger a GET request to the server, with the
+ // query added as a query string.
+ // query: Object
+ // The query to use for retrieving objects from the store.
+ // options: __QueryOptions?
+ // The optional arguments to apply to the resultset.
+ // returns: dojo/store/api/Store.QueryResults
+ // The results of the query, extended with iterative methods.
+ options = options || {};
+
+ var headers = lang.mixin({ Accept: this.accepts }, this.headers, options.headers);
+
+ if(options.start >= 0 || options.count >= 0){
+ headers.Range = headers["X-Range"] //set X-Range for Opera since it blocks "Range" header
+ = "items=" + (options.start || '0') + '-' +
+ (("count" in options && options.count != Infinity) ?
+ (options.count + (options.start || 0) - 1) : '');
+ }
+ var hasQuestionMark = this.target.indexOf("?") > -1;
+ if(query && typeof query == "object"){
+ query = xhr.objectToQuery(query);
+ query = query ? (hasQuestionMark ? "&" : "?") + query: "";
+ }
+ if(options && options.sort){
+ var sortParam = this.sortParam;
+ query += (query || hasQuestionMark ? "&" : "?") + (sortParam ? sortParam + '=' : "sort(");
+ for(var i = 0; i 0 ? "," : "") + (sort.descending ? this.descendingPrefix : this.ascendingPrefix) + encodeURIComponent(sort.attribute);
+ }
+ if(!sortParam){
+ query += ")";
+ }
+ }
+ var results = xhr("GET", {
+ url: this.target + (query || ""),
+ handleAs: "json",
+ headers: headers
+ });
+ results.total = results.then(function(){
+ var range = results.ioArgs.xhr.getResponseHeader("Content-Range");
+ return range && (range = range.match(/\/(.*)/)) && +range[1];
+ });
+ return QueryResults(results);
+ }
+});
+
+});
\ No newline at end of file
diff --git a/src/main/resources/static/dojo/store/Memory.js b/src/main/resources/static/dojo/store/Memory.js
new file mode 100644
index 0000000000000000000000000000000000000000..e135a4ed8fd898e5f567c5e476002af4ff96ee12
--- /dev/null
+++ b/src/main/resources/static/dojo/store/Memory.js
@@ -0,0 +1,164 @@
+define(["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
+function(declare, QueryResults, SimpleQueryEngine /*=====, Store =====*/){
+
+// module:
+// dojo/store/Memory
+
+// No base class, but for purposes of documentation, the base class is dojo/store/api/Store
+var base = null;
+/*===== base = Store; =====*/
+
+return declare("dojo.store.Memory", base, {
+ // summary:
+ // This is a basic in-memory object store. It implements dojo/store/api/Store.
+ constructor: function(options){
+ // summary:
+ // Creates a memory object store.
+ // options: dojo/store/Memory
+ // This provides any configuration information that will be mixed into the store.
+ // This should generally include the data property to provide the starting set of data.
+ for(var i in options){
+ this[i] = options[i];
+ }
+ this.setData(this.data || []);
+ },
+ // data: Array
+ // The array of all the objects in the memory store
+ data:null,
+
+ // idProperty: String
+ // Indicates the property to use as the identity property. The values of this
+ // property should be unique.
+ idProperty: "id",
+
+ // index: Object
+ // An index of data indices into the data array by id
+ index:null,
+
+ // queryEngine: Function
+ // Defines the query engine to use for querying the data store
+ queryEngine: SimpleQueryEngine,
+ get: function(id){
+ // summary:
+ // Retrieves an object by its identity
+ // id: Number
+ // The identity to use to lookup the object
+ // returns: Object
+ // The object in the store that matches the given id.
+ return this.data[this.index[id]];
+ },
+ getIdentity: function(object){
+ // summary:
+ // Returns an object's identity
+ // object: Object
+ // The object to get the identity from
+ // returns: Number
+ return object[this.idProperty];
+ },
+ put: function(object, options){
+ // summary:
+ // Stores an object
+ // object: Object
+ // The object to store.
+ // options: dojo/store/api/Store.PutDirectives?
+ // Additional metadata for storing the data. Includes an "id"
+ // property if a specific id is to be used.
+ // returns: Number
+ var data = this.data,
+ index = this.index,
+ idProperty = this.idProperty;
+ var id = object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
+ if(id in index){
+ // object exists
+ if(options && options.overwrite === false){
+ throw new Error("Object already exists");
+ }
+ // replace the entry in data
+ data[index[id]] = object;
+ }else{
+ // add the new object
+ index[id] = data.push(object) - 1;
+ }
+ return id;
+ },
+ add: function(object, options){
+ // summary:
+ // Creates an object, throws an error if the object already exists
+ // object: Object
+ // The object to store.
+ // options: dojo/store/api/Store.PutDirectives?
+ // Additional metadata for storing the data. Includes an "id"
+ // property if a specific id is to be used.
+ // returns: Number
+ (options = options || {}).overwrite = false;
+ // call put with overwrite being false
+ return this.put(object, options);
+ },
+ remove: function(id){
+ // summary:
+ // Deletes an object by its identity
+ // id: Number
+ // The identity to use to delete the object
+ // returns: Boolean
+ // Returns true if an object was removed, falsy (undefined) if no object matched the id
+ var index = this.index;
+ var data = this.data;
+ if(id in index){
+ data.splice(index[id], 1);
+ // now we have to reindex
+ this.setData(data);
+ return true;
+ }
+ },
+ query: function(query, options){
+ // summary:
+ // Queries the store for objects.
+ // query: Object
+ // The query to use for retrieving objects from the store.
+ // options: dojo/store/api/Store.QueryOptions?
+ // The optional arguments to apply to the resultset.
+ // returns: dojo/store/api/Store.QueryResults
+ // The results of the query, extended with iterative methods.
+ //
+ // example:
+ // Given the following store:
+ //
+ // | var store = new Memory({
+ // | data: [
+ // | {id: 1, name: "one", prime: false },
+ // | {id: 2, name: "two", even: true, prime: true},
+ // | {id: 3, name: "three", prime: true},
+ // | {id: 4, name: "four", even: true, prime: false},
+ // | {id: 5, name: "five", prime: true}
+ // | ]
+ // | });
+ //
+ // ...find all items where "prime" is true:
+ //
+ // | var results = store.query({ prime: true });
+ //
+ // ...or find all items where "even" is true:
+ //
+ // | var results = store.query({ even: true });
+ return QueryResults(this.queryEngine(query, options)(this.data));
+ },
+ setData: function(data){
+ // summary:
+ // Sets the given data as the source for this store, and indexes it
+ // data: Object[]
+ // An array of objects to use as the source of data.
+ if(data.items){
+ // just for convenience with the data format IFRS expects
+ this.idProperty = data.identifier;
+ data = this.data = data.items;
+ }else{
+ this.data = data;
+ }
+ this.index = {};
+ for(var i = 0, l = data.length; i < l; i++){
+ this.index[data[i][this.idProperty]] = i;
+ }
+ }
+});
+
+});
diff --git a/src/main/resources/static/dojo/store/Observable.js b/src/main/resources/static/dojo/store/Observable.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce1469a3ed38ae48b7b1cdf4c523debb516f51af
--- /dev/null
+++ b/src/main/resources/static/dojo/store/Observable.js
@@ -0,0 +1,187 @@
+define(["../_base/kernel", "../_base/lang", "../when", "../_base/array" /*=====, "./api/Store" =====*/
+], function(kernel, lang, when, array /*=====, Store =====*/){
+
+// module:
+// dojo/store/Observable
+
+var Observable = function(/*Store*/ store){
+ // summary:
+ // The Observable store wrapper takes a store and sets an observe method on query()
+ // results that can be used to monitor results for changes.
+ //
+ // description:
+ // Observable wraps an existing store so that notifications can be made when a query
+ // is performed.
+ //
+ // example:
+ // Create a Memory store that returns an observable query, and then log some
+ // information about that query.
+ //
+ // | var store = Observable(new Memory({
+ // | data: [
+ // | {id: 1, name: "one", prime: false},
+ // | {id: 2, name: "two", even: true, prime: true},
+ // | {id: 3, name: "three", prime: true},
+ // | {id: 4, name: "four", even: true, prime: false},
+ // | {id: 5, name: "five", prime: true}
+ // | ]
+ // | }));
+ // | var changes = [], results = store.query({ prime: true });
+ // | var observer = results.observe(function(object, previousIndex, newIndex){
+ // | changes.push({previousIndex:previousIndex, newIndex:newIndex, object:object});
+ // | });
+ //
+ // See the Observable tests for more information.
+
+ var undef, queryUpdaters = [], revision = 0;
+ // a Comet driven store could directly call notify to notify observers when data has
+ // changed on the backend
+ // create a new instance
+ store = lang.delegate(store);
+
+ store.notify = function(object, existingId){
+ revision++;
+ var updaters = queryUpdaters.slice();
+ for(var i = 0, l = updaters.length; i < l; i++){
+ updaters[i](object, existingId);
+ }
+ };
+ var originalQuery = store.query;
+ store.query = function(query, options){
+ options = options || {};
+ var results = originalQuery.apply(this, arguments);
+ if(results && results.forEach){
+ var nonPagedOptions = lang.mixin({}, options);
+ delete nonPagedOptions.start;
+ delete nonPagedOptions.count;
+
+ var queryExecutor = store.queryEngine && store.queryEngine(query, nonPagedOptions);
+ var queryRevision = revision;
+ var listeners = [], queryUpdater;
+ results.observe = function(listener, includeObjectUpdates){
+ if(listeners.push(listener) == 1){
+ // first listener was added, create the query checker and updater
+ queryUpdaters.push(queryUpdater = function(changed, existingId){
+ when(results, function(resultsArray){
+ var atEnd = resultsArray.length != options.count;
+ var i, l, listener;
+ if(++queryRevision != revision){
+ throw new Error("Query is out of date, you must observe() the query prior to any data modifications");
+ }
+ var removedObject, removedFrom = -1, insertedInto = -1;
+ if(existingId !== undef){
+ // remove the old one
+ for(i = 0, l = resultsArray.length; i < l; i++){
+ var object = resultsArray[i];
+ if(store.getIdentity(object) == existingId){
+ removedObject = object;
+ removedFrom = i;
+ if(queryExecutor || !changed){// if it was changed and we don't have a queryExecutor, we shouldn't remove it because updated objects would be eliminated
+ resultsArray.splice(i, 1);
+ }
+ break;
+ }
+ }
+ }
+ if(queryExecutor){
+ // add the new one
+ if(changed &&
+ // if a matches function exists, use that (probably more efficient)
+ (queryExecutor.matches ? queryExecutor.matches(changed) : queryExecutor([changed]).length)){
+
+ var firstInsertedInto = removedFrom > -1 ?
+ removedFrom : // put back in the original slot so it doesn't move unless it needs to (relying on a stable sort below)
+ resultsArray.length;
+ resultsArray.splice(firstInsertedInto, 0, changed); // add the new item
+ insertedInto = array.indexOf(queryExecutor(resultsArray), changed); // sort it
+ // we now need to push the chagne back into the original results array
+ resultsArray.splice(firstInsertedInto, 1); // remove the inserted item from the previous index
+
+ if((options.start && insertedInto == 0) ||
+ (!atEnd && insertedInto == resultsArray.length)){
+ // if it is at the end of the page, assume it goes into the prev or next page
+ insertedInto = -1;
+ }else{
+ resultsArray.splice(insertedInto, 0, changed); // and insert into the results array with the correct index
+ }
+ }
+ }else if(changed){
+ // we don't have a queryEngine, so we can't provide any information
+ // about where it was inserted or moved to. If it is an update, we leave it's position alone, other we at least indicate a new object
+ if(existingId !== undef){
+ // an update, keep the index the same
+ insertedInto = removedFrom;
+ }else if(!options.start){
+ // a new object
+ insertedInto = store.defaultIndex || 0;
+ resultsArray.splice(insertedInto, 0, changed);
+ }
+ }
+ if((removedFrom > -1 || insertedInto > -1) &&
+ (includeObjectUpdates || !queryExecutor || (removedFrom != insertedInto))){
+ var copyListeners = listeners.slice();
+ for(i = 0;listener = copyListeners[i]; i++){
+ listener(changed || removedObject, removedFrom, insertedInto);
+ }
+ }
+ });
+ });
+ }
+ var handle = {};
+ // TODO: Remove cancel in 2.0.
+ handle.remove = handle.cancel = function(){
+ // remove this listener
+ var index = array.indexOf(listeners, listener);
+ if(index > -1){ // check to make sure we haven't already called cancel
+ listeners.splice(index, 1);
+ if(!listeners.length){
+ // no more listeners, remove the query updater too
+ queryUpdaters.splice(array.indexOf(queryUpdaters, queryUpdater), 1);
+ }
+ }
+ };
+ return handle;
+ };
+ }
+ return results;
+ };
+ var inMethod;
+ function whenFinished(method, action){
+ var original = store[method];
+ if(original){
+ store[method] = function(value){
+ if(inMethod){
+ // if one method calls another (like add() calling put()) we don't want two events
+ return original.apply(this, arguments);
+ }
+ inMethod = true;
+ try{
+ var results = original.apply(this, arguments);
+ when(results, function(results){
+ action((typeof results == "object" && results) || value);
+ });
+ return results;
+ }finally{
+ inMethod = false;
+ }
+ };
+ }
+ }
+ // monitor for updates by listening to these methods
+ whenFinished("put", function(object){
+ store.notify(object, store.getIdentity(object));
+ });
+ whenFinished("add", function(object){
+ store.notify(object);
+ });
+ whenFinished("remove", function(id){
+ store.notify(undefined, id);
+ });
+
+ return store;
+};
+
+lang.setObject("dojo.store.Observable", Observable);
+
+return Observable;
+});
diff --git a/src/main/resources/static/dojo/store/README b/src/main/resources/static/dojo/store/README
new file mode 100644
index 0000000000000000000000000000000000000000..4fb5ac724c28a6e1b8af5f05220ade490b262f1b
--- /dev/null
+++ b/src/main/resources/static/dojo/store/README
@@ -0,0 +1,6 @@
+This folder contains the stores and utilities implementing the Dojo Object Store API,
+a successor and unifier to Dojo Data, Dojo Storage, and potentially Dojo Model. These
+stores are designed to provide simple lightweight implementations
+providing core functionality for typical applications.
+
+See http://dojotoolkit.org/features/1.6/object-store for more information
\ No newline at end of file
diff --git a/src/main/resources/static/dojo/store/api/Store.js b/src/main/resources/static/dojo/store/api/Store.js
new file mode 100644
index 0000000000000000000000000000000000000000..470466985d8deaa8ce463a7f55655e913fa8179e
--- /dev/null
+++ b/src/main/resources/static/dojo/store/api/Store.js
@@ -0,0 +1,287 @@
+define(["../../_base/declare"], function(declare){
+
+// module:
+// dojo/api/Store
+
+var Store = declare(null, {
+ // summary:
+ // This is an abstract API that data provider implementations conform to.
+ // This file defines methods signatures and intentionally leaves all the
+ // methods unimplemented. For more information on the ,
+ // please visit: http://dojotoolkit.org/reference-guide/dojo/store.html
+ // Every method and property is optional, and is only needed if the functionality
+ // it provides is required.
+ // Every method may return a promise for the specified return value if the
+ // execution of the operation is asynchronous (except
+ // for query() which already defines an async return value).
+
+ // idProperty: String
+ // If the store has a single primary key, this indicates the property to use as the
+ // identity property. The values of this property should be unique.
+ idProperty: "id",
+
+ // queryEngine: Function
+ // If the store can be queried locally (on the client side in JS), this defines
+ // the query engine to use for querying the data store.
+ // This takes a query and query options and returns a function that can execute
+ // the provided query on a JavaScript array. The queryEngine may be replace to
+ // provide more sophisticated querying capabilities. For example:
+ // | var query = store.queryEngine({foo:"bar"}, {count:10});
+ // | query(someArray) -> filtered array
+ // The returned query function may have a "matches" property that can be
+ // used to determine if an object matches the query. For example:
+ // | query.matches({id:"some-object", foo:"bar"}) -> true
+ // | query.matches({id:"some-object", foo:"something else"}) -> false
+ queryEngine: null,
+
+ get: function(id){
+ // summary:
+ // Retrieves an object by its identity
+ // id: Number
+ // The identity to use to lookup the object
+ // returns: Object
+ // The object in the store that matches the given id.
+ },
+ getIdentity: function(object){
+ // summary:
+ // Returns an object's identity
+ // object: Object
+ // The object to get the identity from
+ // returns: String|Number
+ },
+ put: function(object, directives){
+ // summary:
+ // Stores an object
+ // object: Object
+ // The object to store.
+ // directives: dojo/store/api/Store.PutDirectives?
+ // Additional directives for storing objects.
+ // returns: Number|String
+ },
+ add: function(object, directives){
+ // summary:
+ // Creates an object, throws an error if the object already exists
+ // object: Object
+ // The object to store.
+ // directives: dojo/store/api/Store.PutDirectives?
+ // Additional directives for creating objects.
+ // returns: Number|String
+ },
+ remove: function(id){
+ // summary:
+ // Deletes an object by its identity
+ // id: Number
+ // The identity to use to delete the object
+ delete this.index[id];
+ var data = this.data,
+ idProperty = this.idProperty;
+ for(var i = 0, l = data.length; i < l; i++){
+ if(data[i][idProperty] == id){
+ data.splice(i, 1);
+ return;
+ }
+ }
+ },
+ query: function(query, options){
+ // summary:
+ // Queries the store for objects. This does not alter the store, but returns a
+ // set of data from the store.
+ // query: String|Object|Function
+ // The query to use for retrieving objects from the store.
+ // options: dojo/store/api/Store.QueryOptions
+ // The optional arguments to apply to the resultset.
+ // returns: dojo/store/api/Store.QueryResults
+ // The results of the query, extended with iterative methods.
+ //
+ // example:
+ // Given the following store:
+ //
+ // ...find all items where "prime" is true:
+ //
+ // | store.query({ prime: true }).forEach(function(object){
+ // | // handle each object
+ // | });
+ },
+ transaction: function(){
+ // summary:
+ // Starts a new transaction.
+ // Note that a store user might not call transaction() prior to using put,
+ // delete, etc. in which case these operations effectively could be thought of
+ // as "auto-commit" style actions.
+ // returns: dojo/store/api/Store.Transaction
+ // This represents the new current transaction.
+ },
+ getChildren: function(parent, options){
+ // summary:
+ // Retrieves the children of an object.
+ // parent: Object
+ // The object to find the children of.
+ // options: dojo/store/api/Store.QueryOptions?
+ // Additional options to apply to the retrieval of the children.
+ // returns: dojo/store/api/Store.QueryResults
+ // A result set of the children of the parent object.
+ },
+ getMetadata: function(object){
+ // summary:
+ // Returns any metadata about the object. This may include attribution,
+ // cache directives, history, or version information.
+ // object: Object
+ // The object to return metadata for.
+ // returns: Object
+ // An object containing metadata.
+ }
+});
+
+Store.PutDirectives = declare(null, {
+ // summary:
+ // Directives passed to put() and add() handlers for guiding the update and
+ // creation of stored objects.
+ // id: String|Number?
+ // Indicates the identity of the object if a new object is created
+ // before: Object?
+ // If the collection of objects in the store has a natural ordering,
+ // this indicates that the created or updated object should be placed before the
+ // object specified by the value of this property. A value of null indicates that the
+ // object should be last.
+ // parent: Object?,
+ // If the store is hierarchical (with single parenting) this property indicates the
+ // new parent of the created or updated object.
+ // overwrite: Boolean?
+ // If this is provided as a boolean it indicates that the object should or should not
+ // overwrite an existing object. A value of true indicates that a new object
+ // should not be created, the operation should update an existing object. A
+ // value of false indicates that an existing object should not be updated, a new
+ // object should be created (which is the same as an add() operation). When
+ // this property is not provided, either an update or creation is acceptable.
+});
+
+Store.SortInformation = declare(null, {
+ // summary:
+ // An object describing what attribute to sort on, and the direction of the sort.
+ // attribute: String
+ // The name of the attribute to sort on.
+ // descending: Boolean
+ // The direction of the sort. Default is false.
+});
+
+Store.QueryOptions = declare(null, {
+ // summary:
+ // Optional object with additional parameters for query results.
+ // sort: dojo/store/api/Store.SortInformation[]?
+ // A list of attributes to sort on, as well as direction
+ // For example:
+ // | [{attribute:"price, descending: true}].
+ // If the sort parameter is omitted, then the natural order of the store may be
+ // applied if there is a natural order.
+ // start: Number?
+ // The first result to begin iteration on
+ // count: Number?
+ // The number of how many results should be returned.
+});
+
+Store.QueryResults = declare(null, {
+ // summary:
+ // This is an object returned from query() calls that provides access to the results
+ // of a query. Queries may be executed asynchronously.
+
+ forEach: function(callback, thisObject){
+ // summary:
+ // Iterates over the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+
+ },
+ filter: function(callback, thisObject){
+ // summary:
+ // Filters the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+ // returns: dojo/store/api/Store.QueryResults
+ },
+ map: function(callback, thisObject){
+ // summary:
+ // Maps the query results, based on
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map.
+ // Note that this may executed asynchronously. The callback may be called
+ // after this function returns.
+ // callback:
+ // Function that is called for each object in the query results
+ // thisObject:
+ // The object to use as |this| in the callback.
+ // returns: dojo/store/api/Store.QueryResults
+ },
+ then: function(callback, errorHandler){
+ // summary:
+ // This registers a callback for when the query is complete, if the query is asynchronous.
+ // This is an optional method, and may not be present for synchronous queries.
+ // callback:
+ // This is called when the query is completed successfully, and is passed a single argument
+ // that is an array representing the query results.
+ // errorHandler:
+ // This is called if the query failed, and is passed a single argument that is the error
+ // for the failure.
+ },
+ observe: function(listener, includeAllUpdates){
+ // summary:
+ // This registers a callback for notification of when data is modified in the query results.
+ // This is an optional method, and is usually provided by dojo/store/Observable.
+ // listener: Function
+ // The listener function is called when objects in the query results are modified
+ // to affect the query result. The listener function is called with the following arguments:
+ // | listener(object, removedFrom, insertedInto);
+ //
+ // - The object parameter indicates the object that was create, modified, or deleted.
+ // - The removedFrom parameter indicates the index in the result array where
+ // the object used to be. If the value is -1, then the object is an addition to
+ // this result set (due to a new object being created, or changed such that it
+ // is a part of the result set).
+ // - The insertedInto parameter indicates the index in the result array where
+ // the object should be now. If the value is -1, then the object is a removal
+ // from this result set (due to an object being deleted, or changed such that it
+ // is not a part of the result set).
+ // includeAllUpdates:
+ // This indicates whether or not to include object updates that do not affect
+ // the inclusion or order of the object in the query results. By default this is false,
+ // which means that if any object is updated in such a way that it remains
+ // in the result set and it's position in result sets is not affected, then the listener
+ // will not be fired.
+
+ },
+ // total: Number|Promise?
+ // This property should be included in if the query options included the "count"
+ // property limiting the result set. This property indicates the total number of objects
+ // matching the query (as if "start" and "count" weren't present). This may be
+ // a promise if the query is asynchronous.
+ total: 0
+});
+
+Store.Transaction = declare(null, {
+ // summary:
+ // This is an object returned from transaction() calls that represents the current
+ // transaction.
+
+ commit: function(){
+ // summary:
+ // Commits the transaction. This may throw an error if it fails. Of if the operation
+ // is asynchronous, it may return a promise that represents the eventual success
+ // or failure of the commit.
+ },
+ abort: function(callback, thisObject){
+ // summary:
+ // Aborts the transaction. This may throw an error if it fails. Of if the operation
+ // is asynchronous, it may return a promise that represents the eventual success
+ // or failure of the abort.
+ }
+});
+return Store;
+});
diff --git a/src/main/resources/static/dojo/store/util/QueryResults.js b/src/main/resources/static/dojo/store/util/QueryResults.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b390a64b770218757e728e7b70bea67a956a275
--- /dev/null
+++ b/src/main/resources/static/dojo/store/util/QueryResults.js
@@ -0,0 +1,63 @@
+define(["../../_base/array", "../../_base/lang", "../../when"
+], function(array, lang, when){
+
+// module:
+// dojo/store/util/QueryResults
+
+var QueryResults = function(results){
+ // summary:
+ // A function that wraps the results of a store query with additional
+ // methods.
+ // description:
+ // QueryResults is a basic wrapper that allows for array-like iteration
+ // over any kind of returned data from a query. While the simplest store
+ // will return a plain array of data, other stores may return deferreds or
+ // promises; this wrapper makes sure that *all* results can be treated
+ // the same.
+ //
+ // Additional methods include `forEach`, `filter` and `map`.
+ // results: Array|dojo/promise/Promise
+ // The result set as an array, or a promise for an array.
+ // returns:
+ // An array-like object that can be used for iterating over.
+ // example:
+ // Query a store and iterate over the results.
+ //
+ // | store.query({ prime: true }).forEach(function(item){
+ // | // do something
+ // | });
+
+ if(!results){
+ return results;
+ }
+ // if it is a promise it may be frozen
+ if(results.then){
+ results = lang.delegate(results);
+ }
+ function addIterativeMethod(method){
+ if(!results[method]){
+ results[method] = function(){
+ var args = arguments;
+ return when(results, function(results){
+ Array.prototype.unshift.call(args, results);
+ return QueryResults(array[method].apply(array, args));
+ });
+ };
+ }
+ }
+ addIterativeMethod("forEach");
+ addIterativeMethod("filter");
+ addIterativeMethod("map");
+ if(!results.total){
+ results.total = when(results, function(results){
+ return results.length;
+ });
+ }
+ return results; // Object
+};
+
+lang.setObject("dojo.store.util.QueryResults", QueryResults);
+
+return QueryResults;
+
+});
diff --git a/src/main/resources/static/dojo/store/util/SimpleQueryEngine.js b/src/main/resources/static/dojo/store/util/SimpleQueryEngine.js
new file mode 100644
index 0000000000000000000000000000000000000000..57d67b4fa9b4877bce7439b23fec569e371c5846
--- /dev/null
+++ b/src/main/resources/static/dojo/store/util/SimpleQueryEngine.js
@@ -0,0 +1,110 @@
+define(["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil /*=====, Store =====*/){
+
+// module:
+// dojo/store/util/SimpleQueryEngine
+
+return function(query, options){
+ // summary:
+ // Simple query engine that matches using filter functions, named filter
+ // functions or objects by name-value on a query object hash
+ //
+ // description:
+ // The SimpleQueryEngine provides a way of getting a QueryResults through
+ // the use of a simple object hash as a filter. The hash will be used to
+ // match properties on data objects with the corresponding value given. In
+ // other words, only exact matches will be returned.
+ //
+ // This function can be used as a template for more complex query engines;
+ // for example, an engine can be created that accepts an object hash that
+ // contains filtering functions, or a string that gets evaluated, etc.
+ //
+ // When creating a new dojo.store, simply set the store's queryEngine
+ // field as a reference to this function.
+ //
+ // query: Object
+ // An object hash with fields that may match fields of items in the store.
+ // Values in the hash will be compared by normal == operator, but regular expressions
+ // or any object that provides a test() method are also supported and can be
+ // used to match strings by more complex expressions
+ // (and then the regex's or object's test() method will be used to match values).
+ //
+ // options: dojo/store/api/Store.QueryOptions?
+ // An object that contains optional information such as sort, start, and count.
+ //
+ // returns: Function
+ // A function that caches the passed query under the field "matches". See any
+ // of the "query" methods on dojo.stores.
+ //
+ // example:
+ // Define a store with a reference to this engine, and set up a query method.
+ //
+ // | var myStore = function(options){
+ // | // ...more properties here
+ // | this.queryEngine = SimpleQueryEngine;
+ // | // define our query method
+ // | this.query = function(query, options){
+ // | return QueryResults(this.queryEngine(query, options)(this.data));
+ // | };
+ // | };
+
+ // create our matching query function
+ switch(typeof query){
+ default:
+ throw new Error("Can not query with a " + typeof query);
+ case "object": case "undefined":
+ var queryObject = query;
+ query = function(object){
+ for(var key in queryObject){
+ var required = queryObject[key];
+ if(required && required.test){
+ // an object can provide a test method, which makes it work with regex
+ if(!required.test(object[key], object)){
+ return false;
+ }
+ }else if(required != object[key]){
+ return false;
+ }
+ }
+ return true;
+ };
+ break;
+ case "string":
+ // named query
+ if(!this[query]){
+ throw new Error("No filter function " + query + " was found in store");
+ }
+ query = this[query];
+ // fall through
+ case "function":
+ // fall through
+ }
+ function execute(array){
+ // execute the whole query, first we filter
+ var results = arrayUtil.filter(array, query);
+ // next we sort
+ var sortSet = options && options.sort;
+ if(sortSet){
+ results.sort(typeof sortSet == "function" ? sortSet : function(a, b){
+ for(var sort, i=0; sort = sortSet[i]; i++){
+ var aValue = a[sort.attribute];
+ var bValue = b[sort.attribute];
+ if (aValue != bValue){
+ return !!sort.descending == (aValue == null || aValue > bValue) ? -1 : 1;
+ }
+ }
+ return 0;
+ });
+ }
+ // now we paginate
+ if(options && (options.start || options.count)){
+ var total = results.length;
+ results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
+ results.total = total;
+ }
+ return results;
+ }
+ execute.matches = query;
+ return execute;
+};
+
+});
diff --git a/src/main/resources/static/dojo/string.js b/src/main/resources/static/dojo/string.js
new file mode 100644
index 0000000000000000000000000000000000000000..7784a78ac0bc43cca55e4d43c9709fc3637df921
--- /dev/null
+++ b/src/main/resources/static/dojo/string.js
@@ -0,0 +1,162 @@
+define([
+ "./_base/kernel", // kernel.global
+ "./_base/lang"
+], function(kernel, lang){
+
+// module:
+// dojo/string
+
+var string = {
+ // summary:
+ // String utilities for Dojo
+};
+lang.setObject("dojo.string", string);
+
+string.rep = function(/*String*/str, /*Integer*/num){
+ // summary:
+ // Efficiently replicate a string `n` times.
+ // str:
+ // the string to replicate
+ // num:
+ // number of times to replicate the string
+
+ if(num <= 0 || !str){ return ""; }
+
+ var buf = [];
+ for(;;){
+ if(num & 1){
+ buf.push(str);
+ }
+ if(!(num >>= 1)){ break; }
+ str += str;
+ }
+ return buf.join(""); // String
+};
+
+string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
+ // summary:
+ // Pad a string to guarantee that it is at least `size` length by
+ // filling with the character `ch` at either the start or end of the
+ // string. Pads at the start, by default.
+ // text:
+ // the string to pad
+ // size:
+ // length to provide padding
+ // ch:
+ // character to pad, defaults to '0'
+ // end:
+ // adds padding at the end if true, otherwise pads at start
+ // example:
+ // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
+ // | string.pad("Dojo", 10, "+", true);
+
+ if(!ch){
+ ch = '0';
+ }
+ var out = String(text),
+ pad = string.rep(ch, Math.ceil((size - out.length) / ch.length));
+ return end ? out + pad : pad + out; // String
+};
+
+string.substitute = function( /*String*/ template,
+ /*Object|Array*/map,
+ /*Function?*/ transform,
+ /*Object?*/ thisObject){
+ // summary:
+ // Performs parameterized substitutions on a string. Throws an
+ // exception if any parameter is unmatched.
+ // template:
+ // a string with expressions in the form `${key}` to be replaced or
+ // `${key:format}` which specifies a format function. keys are case-sensitive.
+ // map:
+ // hash to search for substitutions
+ // transform:
+ // a function to process all parameters before substitution takes
+ // place, e.g. mylib.encodeXML
+ // thisObject:
+ // where to look for optional format function; default to the global
+ // namespace
+ // example:
+ // Substitutes two expressions in a string from an Array or Object
+ // | // returns "File 'foo.html' is not found in directory '/temp'."
+ // | // by providing substitution data in an Array
+ // | string.substitute(
+ // | "File '${0}' is not found in directory '${1}'.",
+ // | ["foo.html","/temp"]
+ // | );
+ // |
+ // | // also returns "File 'foo.html' is not found in directory '/temp'."
+ // | // but provides substitution data in an Object structure. Dotted
+ // | // notation may be used to traverse the structure.
+ // | string.substitute(
+ // | "File '${name}' is not found in directory '${info.dir}'.",
+ // | { name: "foo.html", info: { dir: "/temp" } }
+ // | );
+ // example:
+ // Use a transform function to modify the values:
+ // | // returns "file 'foo.html' is not found in directory '/temp'."
+ // | string.substitute(
+ // | "${0} is not found in ${1}.",
+ // | ["foo.html","/temp"],
+ // | function(str){
+ // | // try to figure out the type
+ // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
+ // | return prefix + " '" + str + "'";
+ // | }
+ // | );
+ // example:
+ // Use a formatter
+ // | // returns "thinger -- howdy"
+ // | string.substitute(
+ // | "${0:postfix}", ["thinger"], null, {
+ // | postfix: function(value, key){
+ // | return value + " -- howdy";
+ // | }
+ // | }
+ // | );
+
+ thisObject = thisObject || kernel.global;
+ transform = transform ?
+ lang.hitch(thisObject, transform) : function(v){ return v; };
+
+ return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
+ function(match, key, format){
+ var value = lang.getObject(key, false, map);
+ if(format){
+ value = lang.getObject(format, false, thisObject).call(thisObject, value, key);
+ }
+ return transform(value, key).toString();
+ }); // String
+};
+
+string.trim = String.prototype.trim ?
+ lang.trim : // aliasing to the native function
+ function(str){
+ str = str.replace(/^\s+/, '');
+ for(var i = str.length - 1; i >= 0; i--){
+ if(/\S/.test(str.charAt(i))){
+ str = str.substring(0, i + 1);
+ break;
+ }
+ }
+ return str;
+ };
+
+/*=====
+ string.trim = function(str){
+ // summary:
+ // Trims whitespace from both sides of the string
+ // str: String
+ // String to be trimmed
+ // returns: String
+ // Returns the trimmed string
+ // description:
+ // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
+ // The short yet performant version of this function is dojo.trim(),
+ // which is part of Dojo base. Uses String.prototype.trim instead, if available.
+ return ""; // String
+ };
+ =====*/
+
+ return string;
+});
diff --git a/src/main/resources/static/dojo/tests.js b/src/main/resources/static/dojo/tests.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c68beb29ad286dc22f382513a12d84a87a81a3c
--- /dev/null
+++ b/src/main/resources/static/dojo/tests.js
@@ -0,0 +1,13 @@
+//This file is the command-line entry point for running the tests in
+//Rhino and Spidermonkey.
+
+/*=====
+dojo.tests = {
+ // summary:
+ // D.O.H. Test files for Dojo unit testing.
+};
+=====*/
+
+load("dojo.js");
+load("tests/runner.js");
+tests.run();
diff --git a/src/main/resources/static/dojo/tests/AdapterRegistry.js b/src/main/resources/static/dojo/tests/AdapterRegistry.js
new file mode 100644
index 0000000000000000000000000000000000000000..953d62e181cbf88622c5bbc1e002ed94b3eb3705
--- /dev/null
+++ b/src/main/resources/static/dojo/tests/AdapterRegistry.js
@@ -0,0 +1,72 @@
+define(["doh/main", "../AdapterRegistry"], function(doh, AdapterRegistry){
+
+doh.register("tests.AdapterRegistry",
+ [
+ function ctor(t){
+ var taa = new AdapterRegistry();
+ t.is(0, taa.pairs.length);
+ t.f(taa.returnWrappers);
+
+ var taa = new AdapterRegistry(true);
+ t.t(taa.returnWrappers);
+ },
+
+ function register(t){
+ var taa = new AdapterRegistry();
+ taa.register("blah",
+ function(str){ return str == "blah"; },
+ function(){ return "blah"; }
+ );
+ t.is(1, taa.pairs.length);
+ t.is("blah", taa.pairs[0][0]);
+
+ taa.register("thinger");
+ taa.register("prepend", null, null, true, true);
+ t.is("prepend", taa.pairs[0][0]);
+ t.t(taa.pairs[0][3]);
+ },
+
+ /*
+ function match(t){
+ },
+ */
+
+ function noMatch(t){
+ var taa = new AdapterRegistry();
+ var threw = false;
+ try{
+ taa.match("blah");
+ }catch(e){
+ threw = true;
+ }
+ t.t(threw);
+ },
+
+ function returnWrappers(t){
+ var taa = new AdapterRegistry();
+ taa.register("blah",
+ function(str){ return str == "blah"; },
+ function(){ return "blah"; }
+ );
+ t.is("blah", taa.match("blah"));
+
+ taa.returnWrappers = true;
+ t.is("blah", taa.match("blah")());
+ },
+
+ function unregister(t){
+ var taa = new AdapterRegistry();
+ taa.register("blah",
+ function(str){ return str == "blah"; },
+ function(){ return "blah"; }
+ );
+ taa.register("thinger");
+ taa.register("prepend", null, null, true, true);
+ taa.unregister("prepend");
+ t.is(2, taa.pairs.length);
+ t.is("blah", taa.pairs[0][0]);
+ }
+ ]
+);
+
+});
\ No newline at end of file
diff --git a/src/main/resources/static/dojo/tests/Deferred.js b/src/main/resources/static/dojo/tests/Deferred.js
new file mode 100644
index 0000000000000000000000000000000000000000..d1b8c313884bf333a13e368ed183b89992bac60c
--- /dev/null
+++ b/src/main/resources/static/dojo/tests/Deferred.js
@@ -0,0 +1,477 @@
+define([
+ "doh/main",
+ "dojo/Deferred",
+ "dojo/promise/Promise",
+ "dojo/errors/CancelError"
+], function(doh, Deferred, Promise, CancelError){
+ var tests = {
+ "deferred receives result after resolving": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(function(result){ received = result; });
+ this.deferred.resolve(obj);
+ t.t(received === obj);
+ },
+
+ "promise receives result after resolving": function(t){
+ var obj = {};
+ var received;
+ this.deferred.promise.then(function(result){ received = obj; });
+ this.deferred.resolve(obj);
+ t.t(received === obj);
+ },
+
+ "resolve() returns promise": function(t){
+ var obj = {};
+ var returnedPromise = this.deferred.resolve(obj);
+ t.t(returnedPromise instanceof Promise);
+ t.t(returnedPromise === this.deferred.promise);
+ },
+
+ "isResolved() returns true after resolving": function(t){
+ t.f(this.deferred.isResolved());
+ this.deferred.resolve();
+ t.t(this.deferred.isResolved());
+ },
+
+ "isFulfilled() returns true after resolving": function(t){
+ t.f(this.deferred.isFulfilled());
+ this.deferred.resolve();
+ t.t(this.deferred.isFulfilled());
+ },
+
+ "resolve() is ignored after having been fulfilled": function(t){
+ this.deferred.resolve();
+ this.deferred.resolve();
+ },
+
+ "resolve() throws error after having been fulfilled and strict": function(t){
+ this.deferred.resolve();
+ t.e(Error, this.deferred, "resolve", [{}, true]);
+ },
+
+ "resolve() results are cached": function(t){
+ var obj = {};
+ var received;
+ this.deferred.resolve(obj);
+ this.deferred.then(function(result){ received = result; });
+ t.t(received === obj);
+ },
+
+ "resolve() is already bound to the deferred": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(function(result){ received = result; });
+ var resolve = this.deferred.resolve;
+ resolve(obj);
+ t.t(received === obj);
+ },
+
+ "deferred receives result after rejecting": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(null, function(result){ received = result; });
+ this.deferred.reject(obj);
+ t.t(received === obj);
+ },
+
+ "promise receives result after rejecting": function(t){
+ var obj = {};
+ var received;
+ this.deferred.promise.then(null, function(result){ received = result; });
+ this.deferred.reject(obj);
+ t.t(received === obj);
+ },
+
+ "reject() returns promise": function(t){
+ var obj = {};
+ var returnedPromise = this.deferred.reject(obj);
+ t.t(returnedPromise instanceof Promise);
+ t.t(returnedPromise === this.deferred.promise);
+ },
+
+ "isRejected() returns true after rejecting": function(t){
+ t.f(this.deferred.isRejected());
+ this.deferred.reject();
+ t.t(this.deferred.isRejected());
+ },
+
+ "isFulfilled() returns true after rejecting": function(t){
+ t.f(this.deferred.isFulfilled());
+ this.deferred.reject();
+ t.t(this.deferred.isFulfilled());
+ },
+
+ "reject() is ignored after having been fulfilled": function(t){
+ this.deferred.reject();
+ this.deferred.reject();
+ },
+
+ "reject() throws error after having been fulfilled and strict": function(t){
+ this.deferred.reject();
+ t.e(Error, this.deferred, "reject", [{}, true]);
+ },
+
+ "reject() results are cached": function(t){
+ var obj = {};
+ var received;
+ this.deferred.reject(obj);
+ this.deferred.then(null, function(result){ received = result; });
+ t.t(received === obj);
+ },
+
+ "reject() is already bound to the deferred": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(null, function(result){ received = result; });
+ var reject = this.deferred.reject;
+ reject(obj);
+ t.t(received === obj);
+ },
+
+ "deferred receives result after progress": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(null, null, function(result){ received = result; });
+ this.deferred.progress(obj);
+ t.t(received === obj);
+ },
+
+ "promise receives result after progres": function(t){
+ var obj = {};
+ var received;
+ this.deferred.promise.then(null, null, function(result){ received = result; });
+ this.deferred.progress(obj);
+ t.t(received === obj);
+ },
+
+ "progress() returns promise": function(t){
+ var obj = {};
+ var returnedPromise = this.deferred.progress(obj);
+ t.t(returnedPromise instanceof Promise);
+ t.t(returnedPromise === this.deferred.promise);
+ },
+
+ "isResolved() returns false after progress": function(t){
+ t.f(this.deferred.isResolved());
+ this.deferred.progress();
+ t.f(this.deferred.isResolved());
+ },
+
+ "isRejected() returns false after progress": function(t){
+ t.f(this.deferred.isRejected());
+ this.deferred.progress();
+ t.f(this.deferred.isRejected());
+ },
+
+ "isFulfilled() returns false after progress": function(t){
+ t.f(this.deferred.isFulfilled());
+ this.deferred.progress();
+ t.f(this.deferred.isFulfilled());
+ },
+
+ "progress() is ignored after having been fulfilled": function(t){
+ this.deferred.resolve();
+ this.deferred.resolve();
+ },
+
+ "progress() throws error after having been fulfilled and strict": function(t){
+ this.deferred.resolve();
+ t.e(Error, this.deferred, "progress", [{}, true]);
+ },
+
+ "progress() results are not cached": function(t){
+ var obj1 = {}, obj2 = {};
+ var received = [];
+ this.deferred.progress(obj1);
+ this.deferred.then(null, null, function(result){ received.push(result); });
+ this.deferred.progress(obj2);
+ t.t(received[0] === obj2);
+ t.is(1, received.length);
+ },
+
+ "progress() with chaining": function(t){
+ var obj = {};
+ var inner = new Deferred();
+ var received;
+ this.deferred.then(function(){ return inner; }).then(null, null, function(result){ received = result; });
+ this.deferred.resolve();
+ inner.progress(obj);
+ t.t(received === obj);
+ },
+
+ "after progress(), the progback return value is emitted on the returned promise": function(t){
+ var received;
+ var promise = this.deferred.then(null, null, function(n){ return n * n; });
+ promise.then(null, null, function(n){ received = n; });
+ this.deferred.progress(2);
+ t.is(4, received);
+ },
+
+ "progress() is already bound to the deferred": function(t){
+ var obj = {};
+ var received;
+ this.deferred.then(null, null, function(result){ received = result; });
+ var progress = this.deferred.progress;
+ progress(obj);
+ t.t(received === obj);
+ },
+
+ "cancel() invokes a canceler": function(t){
+ var invoked;
+ this.canceler = function(){ invoked = true; };
+ this.deferred.cancel();
+ t.t(invoked);
+ },
+
+ "isCanceled() returns true after canceling": function(t){
+ t.f(this.deferred.isCanceled());
+ this.deferred.cancel();
+ t.t(this.deferred.isCanceled());
+ },
+
+ "isResolved() returns false after canceling": function(t){
+ t.f(this.deferred.isResolved());
+ this.deferred.cancel();
+ t.f(this.deferred.isResolved());
+ },
+
+ "isRejected() returns true after canceling": function(t){
+ t.f(this.deferred.isRejected());
+ this.deferred.cancel();
+ t.t(this.deferred.isRejected());
+ },
+
+ "isFulfilled() returns true after canceling": function(t){
+ t.f(this.deferred.isFulfilled());
+ this.deferred.cancel();
+ t.t(this.deferred.isFulfilled());
+ },
+
+ "cancel() is ignored after having been fulfilled": function(t){
+ var canceled = false;
+ this.canceler = function(){ canceled = true; };
+ this.deferred.resolve();
+ this.deferred.cancel();
+ t.f(canceled);
+ },
+
+ "cancel() throws error after having been fulfilled and strict": function(t){
+ this.deferred.resolve();
+ t.e(Error, this.deferred, "cancel", [null, true]);
+ },
+
+ "cancel() without reason results in CancelError": function(t){
+ var reason = this.deferred.cancel();
+ var received;
+ this.deferred.then(null, function(result){ received = result; });
+ t.t(received, reason);
+ },
+
+ "cancel() returns default reason": function(t){
+ var reason = this.deferred.cancel();
+ t.t(reason instanceof CancelError);
+ },
+
+ "reason is passed to canceler": function(t){
+ var obj = {};
+ var received;
+ this.canceler = function(reason){ received = reason; };
+ this.deferred.cancel(obj);
+ t.t(received === obj);
+ },
+
+ "cancels with reason returned from canceler": function(t){
+ var obj = {};
+ var received;
+ this.canceler = function(){ return obj; };
+ var reason = this.deferred.cancel();
+ this.deferred.then(null, function(reason){ received = reason; });
+ t.t(received === obj);
+ },
+
+ "cancel() returns reason from canceler": function(t){
+ var obj = {};
+ this.canceler = function(){ return obj; };
+ var reason = this.deferred.cancel();
+ t.t(reason === obj);
+ },
+
+ "cancel() returns reason from canceler, if canceler rejects with reason": function(t){
+ var obj = {};
+ var deferred = this.deferred;
+ this.canceler = function(){ deferred.reject(obj); return obj; };
+ var reason = this.deferred.cancel();
+ t.t(reason === obj);
+ },
+
+ "with canceler not returning anything, returns default CancelError": function(t){
+ this.canceler = function(){};
+ var reason = this.deferred.cancel();
+ var received;
+ this.deferred.then(null, function(result){ received = result; });
+ t.t(received === reason);
+ },
+
+ "with canceler not returning anything, still returns passed reason": function(t){
+ var obj = {};
+ var received;
+ this.canceler = function(){};
+ var reason = this.deferred.cancel(obj);
+ t.t(reason === obj);
+ this.deferred.then(null, function(result){ received = result; });
+ t.t(received === reason);
+ },
+
+ "cancel() doesn't reject promise if canceler resolves deferred": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ var received;
+ this.canceler = function(){ deferred.resolve(obj); };
+ this.deferred.cancel();
+ this.deferred.then(function(result){ received = result; });
+ t.t(received === obj);
+ },
+
+ "cancel() doesn't reject promise if canceler resolves a chain of promises": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ var received;
+ this.canceler = function(){ deferred.resolve(obj); };
+ var last = this.deferred.then().then().then();
+ last.cancel();
+ last.then(function(result){ received = result; });
+ t.t(received === obj);
+ t.t(this.deferred.isCanceled());
+ t.t(last.isCanceled());
+ },
+
+ "cancel() returns undefined if canceler resolves deferred": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ this.canceler = function(){ deferred.resolve(obj); };
+ var result = this.deferred.cancel();
+ t.t(typeof result === "undefined");
+ },
+
+ "cancel() doesn't change rejection value if canceler rejects deferred": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ var received;
+ this.canceler = function(){ deferred.reject(obj); };
+ this.deferred.cancel();
+ this.deferred.then(null, function(result){ received = result; });
+ t.t(received === obj);
+ },
+
+ "cancel() doesn't change rejection value if canceler rejects a chain of promises": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ var received;
+ this.canceler = function(){ deferred.reject(obj); };
+ var last = this.deferred.then().then().then();
+ last.cancel();
+ last.then(null, function(result){ received = result; });
+ t.t(received === obj);
+ t.t(this.deferred.isCanceled());
+ t.t(last.isCanceled());
+ },
+
+ "cancel() returns undefined if canceler rejects deferred": function(t){
+ var deferred = this.deferred;
+ var obj = {};
+ this.canceler = function(){ deferred.reject(obj); };
+ var result = this.deferred.cancel();
+ t.t(typeof result === "undefined");
+ },
+
+ "cancel() a promise chain": function(t){
+ var obj = {};
+ var received;
+ this.canceler = function(reason){ received = reason; };
+ this.deferred.then().then().then().cancel(obj);
+ t.t(received === obj);
+ },
+
+ "cancel() a returned promise": function(t){
+ var obj = {};
+ var received;
+ var inner = new Deferred(function(reason){ received = reason; });
+ var chain = this.deferred.then(function(){
+ return inner;
+ });
+ this.deferred.resolve();
+ chain.cancel(obj, true);
+ t.t(received === obj);
+ },
+
+ "cancel() is already bound to the deferred": function(t){
+ var received;
+ this.deferred.then(null, function(result){ received = result; });
+ var cancel = this.deferred.cancel;
+ cancel();
+ t.t(received instanceof CancelError);
+ },
+
+ "chained then()": function(t){
+ function square(n){ return n * n; }
+
+ var result;
+ this.deferred.then(square).then(square).then(function(n){
+ result = n;
+ });
+ this.deferred.resolve(2);
+ t.is(result, 16);
+ },
+
+ "asynchronously chained then()": function(t){
+ function asyncSquare(n){
+ var inner = new Deferred();
+ setTimeout(function(){ inner.resolve(n * n); }, 0);
+ return inner.promise;
+ }
+
+ var td = new doh.Deferred();
+ this.deferred.then(asyncSquare).then(asyncSquare).then(function(n){
+ t.is(n, 16);
+ td.callback(true);
+ });
+ this.deferred.resolve(2);
+ return td;
+ },
+
+ "then() is already bound to the deferred": function(t){
+ var obj = {};
+ var then = this.deferred.then;
+ var received;
+ then(function(result){ received = result; });
+ this.deferred.resolve(obj);
+ t.t(received === obj);
+ },
+
+ "then() with progback: returned promise is not fulfilled when progress is emitted": function(t){
+ var progressed = false;
+ var promise = this.deferred.then(null, null, function(){ progressed = true; });
+ this.deferred.progress();
+ t.t(progressed, "Progress was received.");
+ t.f(promise.isFulfilled(), "Promise is not fulfilled.");
+ }
+ };
+
+ var wrapped = [];
+ for(var name in tests){
+ wrapped.push({
+ name: name,
+ setUp: setUp,
+ runTest: tests[name]
+ });
+ }
+
+ function setUp(){
+ var self = this;
+ this.canceler = function(reason){};
+ this.deferred = new Deferred(function(reason){ return self.canceler(reason); });
+ }
+
+ doh.register("tests.Deferred", wrapped);
+});
diff --git a/src/main/resources/static/dojo/tests/DeferredList.js b/src/main/resources/static/dojo/tests/DeferredList.js
new file mode 100644
index 0000000000000000000000000000000000000000..4458aa7990571873260c23be8df2d5df15fb837b
--- /dev/null
+++ b/src/main/resources/static/dojo/tests/DeferredList.js
@@ -0,0 +1,193 @@
+define(["../main", "doh/main", "../DeferredList"], function(dojo, doh){
+ doh.register("tests.DeferredList", [
+ function callback(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ console.log("res: ", res, res.length);
+ t.assertTrue(res.length == 2);
+ t.assertTrue(res[0][0]);
+ t.assertEqual(res[0][1], "foo");
+ t.assertTrue(res[1][0]);
+ t.assertEqual(res[1][1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.callback("foo");
+ d2.callback("bar");
+ t.assertTrue(fired);
+ },
+
+ function errback(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+ var e1 = new Error("foo");
+ var e2 = new Error("bar");
+
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ t.assertTrue(res.length == 2);
+ t.assertTrue(!res[0][0]);
+
+ t.assertEqual(res[0][1], e1);
+ t.assertTrue(!res[1][0]);
+ t.assertEqual(res[1][1], e2);
+ fired = true;
+ return res;
+ });
+ d1.errback(e1);
+ d2.errback(e2);
+ t.assertTrue(fired);
+ },
+
+
+ function mixed(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+ var e = new Error("foo");
+
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ t.assertTrue(res.length == 2);
+ t.assertTrue(!res[0][0]);
+
+ t.assertEqual(res[0][1], e);
+ t.assertTrue(res[1][0]);
+ t.assertEqual(res[1][1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.errback(e);
+ d2.callback("bar");
+ t.assertTrue(fired);
+ },
+
+ function gather(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = dojo.DeferredList.prototype.gatherResults([d1, d2]);
+ var fired = false;
+ dl.addCallback(function(res){
+ t.assertEqual(res[0], "foo");
+ t.assertEqual(res[1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.callback("foo");
+ d2.callback("bar");
+ t.assertTrue(fired);
+ }
+ ]);
+
+ doh.register("tests.DeferredList", [
+ function callback(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ t.assertTrue(res.length == 2);
+ t.assertTrue(res[0][0]);
+ t.assertEqual(res[0][1], "foo");
+ t.assertTrue(res[1][0]);
+ t.assertEqual(res[1][1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.callback("foo");
+ d2.callback("bar");
+ t.assertTrue(fired);
+ },
+
+ function errback(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+ var e1 = new Error("foo");
+ var e2 = new Error("bar");
+
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ t.assertTrue(res.length == 2);
+ t.assertTrue(!res[0][0]);
+
+ t.assertEqual(res[0][1], e1);
+ t.assertTrue(!res[1][0]);
+ t.assertEqual(res[1][1], e2);
+ fired = true;
+ return res;
+ });
+ d1.errback(e1);
+ d2.errback(e2);
+ t.assertTrue(fired);
+ },
+
+
+ function mixed(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = new dojo.DeferredList([d1, d2]);
+ var fired = false;
+ var e = new Error("foo");
+
+ dl.addCallback(function(res){
+ doh.debug("debug from dojo.DeferredList callback");
+ return res;
+ });
+ dl.addCallback(function(res){
+ t.assertTrue(res.length == 2);
+ t.assertTrue(!res[0][0]);
+
+ t.assertEqual(res[0][1], e);
+ t.assertTrue(res[1][0]);
+ t.assertEqual(res[1][1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.errback(e);
+ d2.callback("bar");
+ t.assertTrue(fired);
+ },
+
+ function gather(t){
+ var d1 = new dojo.Deferred();
+ var d2 = new dojo.Deferred();
+ var dl = dojo.DeferredList.prototype.gatherResults([d1, d2]);
+ var fired = false;
+ dl.addCallback(function(res){
+ t.assertEqual(res[0], "foo");
+ t.assertEqual(res[1], "bar");
+ fired = true;
+ return res;
+ });
+ d1.callback("foo");
+ d2.callback("bar");
+ t.assertTrue(fired);
+ }
+ ]);
+});
\ No newline at end of file
diff --git a/src/main/resources/static/dojo/tests/NodeList-data.html b/src/main/resources/static/dojo/tests/NodeList-data.html
new file mode 100644
index 0000000000000000000000000000000000000000..3acde01c1fca0d8b27d5f4a6ca2bd87eeca92856
--- /dev/null
+++ b/src/main/resources/static/dojo/tests/NodeList-data.html
@@ -0,0 +1,203 @@
+
+
+
+ Testing dojo._data / NodeList.data
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean semper
+ sagittis velit. Cras in mi. Duis porta mauris ut ligula. Proin porta
+ rutrum lacus. Etiam consequat scelerisque quam. Nulla facilisi.
+ Maecenas luctus venenatis nulla. In sit amet dui non mi semper iaculis.
+ Sed molestie tortor at ipsum. Morbi dictum rutrum magna. Sed vitae
+ risus.
+
+
+ Aliquam vitae enim. Duis scelerisque metus auctor est venenatis
+ imperdiet. Fusce dignissim porta augue. Nulla vestibulum. Integer lorem
+ nunc, ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in
+ massa bibendum suscipit. Integer eros. Nullam suscipit mauris. In
+ pellentesque. Mauris ipsum est, pharetra semper, pharetra in, viverra
+ quis, tellus. Etiam purus. Quisque egestas, tortor ac cursus lacinia,
+ felis leo adipiscing nisi, et rhoncus elit dolor eget eros. Fusce ut
+ quam. Suspendisse eleifend leo vitae ligula. Nulla facilisi. Nulla
+ rutrum, erat vitae lacinia dictum, pede purus imperdiet lacus, ut
+ semper velit ante id metus. Praesent massa dolor, porttitor sed,
+ pulvinar in, consequat ut, leo. Nullam nec est. Aenean id risus blandit
+ tortor pharetra congue. Suspendisse pulvinar.
+
+
+ Aliquam vitae enim. Duis scelerisque metus auctor est venenatis
+ imperdiet. Fusce dignissim porta augue. Nulla vestibulum. Integer lorem
+ nunc, ullamcorper a, commodo ac, malesuada sed, dolor. Aenean id mi in
+ massa bibendum suscipit. Integer eros. Nullam suscipit mauris. In
+ pellentesque. Mauris ipsum est, pharetra semper, pharetra in, viverra
+ quis, tellus. Etiam purus. Quisque egestas, tortor ac cursus lacinia,
+ felis leo adipiscing nisi, et rhoncus elit dolor eget eros. Fusce ut
+ quam. Suspendisse eleifend leo vitae ligula. Nulla facilisi. Nulla
+ rutrum, erat vitae lacinia dictum, pede purus imperdiet lacus, ut
+ semper velit ante id metus. Praesent massa dolor, porttitor sed,
+ pulvinar in, consequat ut, leo. Nullam nec est. Aenean id risus blandit
+ tortor pharetra congue. Suspendisse pulvinar.
+