require({cache:{ 'url:dijit/templates/CheckedMenuItem.html':"\r\n\t\r\n\t\t\"\"\r\n\t\t\r\n\t\r\n\t\r\n\t\r\n\t \r\n\r\n", 'dijit/form/TextBox':function(){ require({cache:{ 'url:dijit/form/templates/TextBox.html':"
\r\n"}}); define("dijit/form/TextBox", [ "dojo/_base/declare", // declare "dojo/dom-construct", // domConstruct.create "dojo/dom-style", // domStyle.getComputedStyle "dojo/_base/kernel", // kernel.deprecated "dojo/_base/lang", // lang.hitch "dojo/_base/sniff", // has("ie") has("mozilla") "dojo/_base/window", // win.doc.selection.createRange "./_FormValueWidget", "./_TextBoxMixin", "dojo/text!./templates/TextBox.html", ".." // to export dijit._setSelectionRange, remove in 2.0 ], function(declare, domConstruct, domStyle, kernel, lang, has, win, _FormValueWidget, _TextBoxMixin, template, dijit){ /*===== var _FormValueWidget = dijit.form._FormValueWidget; var _TextBoxMixin = dijit.form._TextBoxMixin; =====*/ // module: // dijit/form/TextBox // summary: // A base class for textbox form inputs var TextBox = declare(/*====="dijit.form.TextBox", =====*/ [_FormValueWidget, _TextBoxMixin], { // summary: // A base class for textbox form inputs templateString: template, _singleNodeTemplate: '', _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events baseClass: "dijitTextBox", postMixInProperties: function(){ var type = this.type.toLowerCase(); if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){ this.templateString = this._singleNodeTemplate; } this.inherited(arguments); }, _onInput: function(e){ this.inherited(arguments); if(this.intermediateChanges){ // _TextBoxMixin uses onInput var _this = this; // the setTimeout allows the key to post to the widget input box setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0); } }, _setPlaceHolderAttr: function(v){ this._set("placeHolder", v); if(!this._phspan){ this._attachPoints.push('_phspan'); // dijitInputField class gives placeHolder same padding as the input field // parent node already has dijitInputField class but it doesn't affect this // since it's position: absolute. this._phspan = domConstruct.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after'); } this._phspan.innerHTML=""; this._phspan.appendChild(document.createTextNode(v)); this._updatePlaceHolder(); }, _updatePlaceHolder: function(){ if(this._phspan){ this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none"; } }, _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){ this.inherited(arguments); this._updatePlaceHolder(); }, getDisplayedValue: function(){ // summary: // Deprecated. Use get('displayedValue') instead. // tags: // deprecated kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0"); return this.get('displayedValue'); }, setDisplayedValue: function(/*String*/ value){ // summary: // Deprecated. Use set('displayedValue', ...) instead. // tags: // deprecated kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0"); this.set('displayedValue', value); }, _onBlur: function(e){ if(this.disabled){ return; } this.inherited(arguments); this._updatePlaceHolder(); }, _onFocus: function(/*String*/ by){ if(this.disabled || this.readOnly){ return; } this.inherited(arguments); this._updatePlaceHolder(); } }); if(has("ie")){ TextBox = declare(/*===== "dijit.form.TextBox.IEMixin", =====*/ TextBox, { declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass _isTextSelected: function(){ var range = win.doc.selection.createRange(); var parent = range.parentElement(); return parent == this.textbox && range.text.length == 0; }, postCreate: function(){ this.inherited(arguments); // IE INPUT tag fontFamily has to be set directly using STYLE // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance setTimeout(lang.hitch(this, function(){ try{ var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed if(s){ var ff = s.fontFamily; if(ff){ var inputs = this.domNode.getElementsByTagName("INPUT"); if(inputs){ for(var i=0; i < inputs.length; i++){ inputs[i].style.fontFamily = ff; } } } } }catch(e){/*when used in a Dialog, and this is called before the dialog is shown, s.fontFamily would trigger "Invalid Argument" error.*/} }), 0); } }); // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?) dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){ if(element.createTextRange){ var r = element.createTextRange(); r.collapse(true); r.moveStart("character", -99999); // move to 0 r.moveStart("character", start); // delta from 0 is the correct position r.moveEnd("character", stop-start); r.select(); } } }else if(has("mozilla")){ TextBox = declare(/*===== "dijit.form.TextBox.MozMixin", =====*/TextBox, { declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass _onBlur: function(e){ this.inherited(arguments); if(this.selectOnClick){ // clear selection so that the next mouse click doesn't reselect this.textbox.selectionStart = this.textbox.selectionEnd = undefined; } } }); }else{ TextBox.prototype.declaredClass = "dijit.form.TextBox"; } lang.setObject("dijit.form.TextBox", TextBox); // don't do direct assignment, it confuses API doc parser return TextBox; }); }, 'dojox/grid/DataGrid':function(){ define("dojox/grid/DataGrid", [ "../main", "dojo/_base/array", "dojo/_base/lang", "dojo/_base/json", "dojo/_base/sniff", "dojo/_base/declare", "./_Grid", "./DataSelection", "dojo/_base/html" ], function(dojox, array, lang, json, has, declare, _Grid, DataSelection, html){ /*===== declare("dojox.grid.__DataCellDef", dojox.grid.__CellDef, { constructor: function(){ // field: String? // The attribute to read from the dojo.data item for the row. // fields: String[]? // An array of fields to grab the values of and pass as an array to the grid // get: Function? // function(rowIndex, item?){} rowIndex is of type Integer, item is of type // Object. This function will be called when a cell requests data. Returns // the unformatted data for the cell. } }); =====*/ /*===== declare("dojox.grid.__DataViewDef", dojox.grid.__ViewDef, { constructor: function(){ // cells: dojox.grid.__DataCellDef[]|Array[dojox.grid.__DataCellDef[]]? // The structure of the cells within this grid. // defaultCell: dojox.grid.__DataCellDef? // A cell definition with default values for all cells in this view. If // a property is defined in a cell definition in the "cells" array and // this property, the cell definition's property will override this // property's property. } }); =====*/ var DataGrid = declare("dojox.grid.DataGrid", _Grid, { store: null, query: null, queryOptions: null, fetchText: '...', sortFields: null, // updateDelay: int // Time, in milliseconds, to delay updates automatically so that multiple // calls to onSet/onNew/onDelete don't keep rerendering the grid. Set // to 0 to immediately cause updates. A higher value will result in // better performance at the expense of responsiveness of the grid. updateDelay: 1, /*===== // structure: dojox.grid.__DataViewDef|dojox.grid.__DataViewDef[]|dojox.grid.__DataCellDef[]|Array[dojox.grid.__DataCellDef[]] // View layout defintion. structure: '', =====*/ // You can specify items instead of a query, if you like. They do not need // to be loaded - but the must be items in the store items: null, _store_connects: null, _by_idty: null, _by_idx: null, _cache: null, _pages: null, _pending_requests: null, _bop: -1, _eop: -1, _requests: 0, rowCount: 0, _isLoaded: false, _isLoading: false, //keepSelection: Boolean // Whether keep selection after sort, filter etc. keepSelection: false, postCreate: function(){ this._pages = []; this._store_connects = []; this._by_idty = {}; this._by_idx = []; this._cache = []; this._pending_requests = {}; this._setStore(this.store); this.inherited(arguments); }, destroy: function(){ this.selection.destroy(); this.inherited(arguments); }, createSelection: function(){ this.selection = new DataSelection(this); }, get: function(inRowIndex, inItem){ // summary: Default data getter. // description: // Provides data to display in a grid cell. Called in grid cell context. // So this.cell.index is the column index. // inRowIndex: Integer // Row for which to provide data // returns: // Data to display for a given grid cell. if(inItem && this.field == "_item" && !this.fields){ return inItem; }else if(inItem && this.fields){ var ret = []; var s = this.grid.store; array.forEach(this.fields, function(f){ ret = ret.concat(s.getValues(inItem, f)); }); return ret; }else if(!inItem && typeof inRowIndex === "string"){ return this.inherited(arguments); } return (!inItem ? this.defaultValue : (!this.field ? this.value : (this.field == "_item" ? inItem : this.grid.store.getValue(inItem, this.field)))); }, _checkUpdateStatus: function(){ if(this.updateDelay > 0){ var iStarted = false; if(this._endUpdateDelay){ clearTimeout(this._endUpdateDelay); delete this._endUpdateDelay; iStarted = true; } if(!this.updating){ this.beginUpdate(); iStarted = true; } if(iStarted){ var _this = this; this._endUpdateDelay = setTimeout(function(){ delete _this._endUpdateDelay; _this.endUpdate(); }, this.updateDelay); } } }, _onSet: function(item, attribute, oldValue, newValue){ this._checkUpdateStatus(); var idx = this.getItemIndex(item); if(idx>-1){ this.updateRow(idx); } }, _createItem: function(item, index){ var idty = this._hasIdentity ? this.store.getIdentity(item) : json.toJson(this.query) + ":idx:" + index + ":sort:" + json.toJson(this.getSortProps()); var o = this._by_idty[idty] = { idty: idty, item: item }; return o; }, _addItem: function(item, index, noUpdate){ this._by_idx[index] = this._createItem(item, index); if(!noUpdate){ this.updateRow(index); } }, _onNew: function(item, parentInfo){ this._checkUpdateStatus(); var rowCount = this.get('rowCount'); this._addingItem = true; this.updateRowCount(rowCount+1); this._addingItem = false; this._addItem(item, rowCount); this.showMessage(); }, _onDelete: function(item){ this._checkUpdateStatus(); var idx = this._getItemIndex(item, true); if(idx >= 0){ // When a row is deleted, all rest rows are shifted down, // and migrate from page to page. If some page is not // loaded yet empty rows can migrate to initialized pages // without refreshing. It causes empty rows in some pages, see: // http://bugs.dojotoolkit.org/ticket/6818 // this code fix this problem by reseting loaded page info this._pages = []; this._bop = -1; this._eop = -1; var o = this._by_idx[idx]; this._by_idx.splice(idx, 1); delete this._by_idty[o.idty]; this.updateRowCount(this.get('rowCount')-1); if(this.get('rowCount') === 0){ this.showMessage(this.noDataMessage); } } if(this.selection.isSelected(idx)){ this.selection.deselect(idx); this.selection.selected.splice(idx, 1); } }, _onRevert: function(){ this._refresh(); }, setStore: function(store, query, queryOptions){ if(this._requestsPending(0)){ return; } this._setQuery(query, queryOptions); this._setStore(store); this._refresh(true); }, setQuery: function(query, queryOptions){ if(this._requestsPending(0)){ return; } this._setQuery(query, queryOptions); this._refresh(true); }, setItems: function(items){ this.items = items; this._setStore(this.store); this._refresh(true); }, _setQuery: function(query, queryOptions){ this.query = query; this.queryOptions = queryOptions || this.queryOptions; }, _setStore: function(store){ if(this.store && this._store_connects){ array.forEach(this._store_connects, this.disconnect, this); } this.store = store; if(this.store){ var f = this.store.getFeatures(); var h = []; this._canEdit = !!f["dojo.data.api.Write"] && !!f["dojo.data.api.Identity"]; this._hasIdentity = !!f["dojo.data.api.Identity"]; if(!!f["dojo.data.api.Notification"] && !this.items){ h.push(this.connect(this.store, "onSet", "_onSet")); h.push(this.connect(this.store, "onNew", "_onNew")); h.push(this.connect(this.store, "onDelete", "_onDelete")); } if(this._canEdit){ h.push(this.connect(this.store, "revert", "_onRevert")); } this._store_connects = h; } }, _onFetchBegin: function(size, req){ if(!this.scroller){ return; } if(this.rowCount != size){ if(req.isRender){ this.scroller.init(size, this.keepRows, this.rowsPerPage); this.rowCount = size; this._setAutoHeightAttr(this.autoHeight, true); this._skipRowRenormalize = true; this.prerender(); this._skipRowRenormalize = false; }else{ this.updateRowCount(size); } } if(!size){ this.views.render(); this._resize(); this.showMessage(this.noDataMessage); this.focus.initFocusView(); }else{ this.showMessage(); } }, _onFetchComplete: function(items, req){ if(!this.scroller){ return; } if(items && items.length > 0){ //console.log(items); array.forEach(items, function(item, idx){ this._addItem(item, req.start+idx, true); }, this); this.updateRows(req.start, items.length); if(req.isRender){ this.setScrollTop(0); this.postrender(); }else if(this._lastScrollTop){ this.setScrollTop(this._lastScrollTop); } if(has("ie")){ html.setSelectable(this.domNode, this.selectable); } } delete this._lastScrollTop; if(!this._isLoaded){ this._isLoading = false; this._isLoaded = true; } this._pending_requests[req.start] = false; }, _onFetchError: function(err, req){ console.log(err); delete this._lastScrollTop; if(!this._isLoaded){ this._isLoading = false; this._isLoaded = true; this.showMessage(this.errorMessage); } this._pending_requests[req.start] = false; this.onFetchError(err, req); }, onFetchError: function(err, req){ }, _fetch: function(start, isRender){ start = start || 0; if(this.store && !this._pending_requests[start]){ if(!this._isLoaded && !this._isLoading){ this._isLoading = true; this.showMessage(this.loadingMessage); } this._pending_requests[start] = true; //console.log("fetch: ", start); try{ if(this.items){ var items = this.items; var store = this.store; this.rowsPerPage = items.length; var req = { start: start, count: this.rowsPerPage, isRender: isRender }; this._onFetchBegin(items.length, req); // Load them if we need to var waitCount = 0; array.forEach(items, function(i){ if(!store.isItemLoaded(i)){ waitCount++; } }); if(waitCount === 0){ this._onFetchComplete(items, req); }else{ var onItem = function(item){ waitCount--; if(waitCount === 0){ this._onFetchComplete(items, req); } }; array.forEach(items, function(i){ if(!store.isItemLoaded(i)){ store.loadItem({item: i, onItem: onItem, scope: this}); } }, this); } }else{ this.store.fetch({ start: start, count: this.rowsPerPage, query: this.query, sort: this.getSortProps(), queryOptions: this.queryOptions, isRender: isRender, onBegin: lang.hitch(this, "_onFetchBegin"), onComplete: lang.hitch(this, "_onFetchComplete"), onError: lang.hitch(this, "_onFetchError") }); } }catch(e){ this._onFetchError(e, {start: start, count: this.rowsPerPage}); } } }, _clearData: function(){ this.updateRowCount(0); this._by_idty = {}; this._by_idx = []; this._pages = []; this._bop = this._eop = -1; this._isLoaded = false; this._isLoading = false; }, getItem: function(idx){ var data = this._by_idx[idx]; if(!data||(data&&!data.item)){ this._preparePage(idx); return null; } return data.item; }, getItemIndex: function(item){ return this._getItemIndex(item, false); }, _getItemIndex: function(item, isDeleted){ if(!isDeleted && !this.store.isItem(item)){ return -1; } var idty = this._hasIdentity ? this.store.getIdentity(item) : null; for(var i=0, l=this._by_idx.length; i= this._eop) && !this._addingItem){ var pageIndex = this._rowToPage(inRowIndex); this._needPage(pageIndex); this._bop = pageIndex * this.rowsPerPage; this._eop = this._bop + (this.rowsPerPage || this.get('rowCount')); } }, _needPage: function(inPageIndex){ if(!this._pages[inPageIndex]){ this._pages[inPageIndex] = true; this._requestPage(inPageIndex); } }, _requestPage: function(inPageIndex){ var row = this._pageToRow(inPageIndex); var count = Math.min(this.rowsPerPage, this.get('rowCount') - row); if(count > 0){ this._requests++; if(!this._requestsPending(row)){ setTimeout(lang.hitch(this, "_fetch", row, false), 1); //this.requestRows(row, count); } } }, getCellName: function(inCell){ return inCell.field; //console.log(inCell); }, _refresh: function(isRender){ this._clearData(); this._fetch(0, isRender); }, sort: function(){ this.edit.apply(); this._lastScrollTop = this.scrollTop; this._refresh(); }, canSort: function(){ return (!this._isLoading); }, getSortProps: function(){ var c = this.getCell(this.getSortIndex()); if(!c){ if(this.sortFields){ return this.sortFields; } return null; }else{ var desc = c["sortDesc"]; var si = !(this.sortInfo>0); if(typeof desc == "undefined"){ desc = si; }else{ desc = si ? !desc : desc; } return [{ attribute: c.field, descending: desc }]; } }, styleRowState: function(inRow){ // summary: Perform row styling if(this.store && this.store.getState){ var states=this.store.getState(inRow.index), c=''; for(var i=0, ss=["inflight", "error", "inserting"], s; s=ss[i]; i++){ if(states[s]){ c = ' dojoxGridRow-' + s; break; } } inRow.customClasses += c; } }, onStyleRow: function(inRow){ this.styleRowState(inRow); this.inherited(arguments); }, // editing canEdit: function(inCell, inRowIndex){ return this._canEdit; }, _copyAttr: function(idx, attr){ var row = {}; var backstop = {}; var src = this.getItem(idx); return this.store.getValue(src, attr); }, doStartEdit: function(inCell, inRowIndex){ if(!this._cache[inRowIndex]){ this._cache[inRowIndex] = this._copyAttr(inRowIndex, inCell.field); } this.onStartEdit(inCell, inRowIndex); }, doApplyCellEdit: function(inValue, inRowIndex, inAttrName){ this.store.fetchItemByIdentity({ identity: this._by_idx[inRowIndex].idty, onItem: lang.hitch(this, function(item){ var oldValue = this.store.getValue(item, inAttrName); if(typeof oldValue == 'number'){ inValue = isNaN(inValue) ? inValue : parseFloat(inValue); }else if(typeof oldValue == 'boolean'){ inValue = inValue == 'true' ? true : inValue == 'false' ? false : inValue; }else if(oldValue instanceof Date){ var asDate = new Date(inValue); inValue = isNaN(asDate.getTime()) ? inValue : asDate; } this.store.setValue(item, inAttrName, inValue); this.onApplyCellEdit(inValue, inRowIndex, inAttrName); }) }); }, doCancelEdit: function(inRowIndex){ var cache = this._cache[inRowIndex]; if(cache){ this.updateRow(inRowIndex); delete this._cache[inRowIndex]; } this.onCancelEdit.apply(this, arguments); }, doApplyEdit: function(inRowIndex, inDataAttr){ var cache = this._cache[inRowIndex]; /*if(cache){ var data = this.getItem(inRowIndex); if(this.store.getValue(data, inDataAttr) != cache){ this.update(cache, data, inRowIndex); } delete this._cache[inRowIndex]; }*/ this.onApplyEdit(inRowIndex); }, removeSelectedRows: function(){ // summary: // Remove the selected rows from the grid. if(this._canEdit){ this.edit.apply(); var fx = lang.hitch(this, function(items){ if(items.length){ array.forEach(items, this.store.deleteItem, this.store); this.selection.clear(); } }); if(this.allItemsSelected){ this.store.fetch({ query: this.query, queryOptions: this.queryOptions, onComplete: fx}); }else{ fx(this.selection.getSelected()); } } } }); DataGrid.cell_markupFactory = function(cellFunc, node, cellDef){ var field = lang.trim(html.attr(node, "field")||""); if(field){ cellDef.field = field; } cellDef.field = cellDef.field||cellDef.name; var fields = lang.trim(html.attr(node, "fields")||""); if(fields){ cellDef.fields = fields.split(","); } if(cellFunc){ cellFunc(node, cellDef); } }; DataGrid.markupFactory = function(props, node, ctor, cellFunc){ return _Grid.markupFactory(props, node, ctor, lang.partial(DataGrid.cell_markupFactory, cellFunc)); }; return DataGrid; }); }, 'dijit/_TemplatedMixin':function(){ define("dijit/_TemplatedMixin", [ "dojo/_base/lang", // lang.getObject "dojo/touch", "./_WidgetBase", "dojo/string", // string.substitute string.trim "dojo/cache", // dojo.cache "dojo/_base/array", // array.forEach "dojo/_base/declare", // declare "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom "dojo/_base/sniff", // has("ie") "dojo/_base/unload", // unload.addOnWindowUnload "dojo/_base/window" // win.doc ], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload, win) { /*===== var _WidgetBase = dijit._WidgetBase; =====*/ // module: // dijit/_TemplatedMixin // summary: // Mixin for widgets that are instantiated from a template var _TemplatedMixin = declare("dijit._TemplatedMixin", null, { // summary: // Mixin for widgets that are instantiated from a template // templateString: [protected] String // A string that represents the widget template. // Use in conjunction with dojo.cache() to load from a file. templateString: null, // templatePath: [protected deprecated] String // Path to template (HTML file) for this widget relative to dojo.baseUrl. // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead templatePath: null, // skipNodeCache: [protected] Boolean // If using a cached widget template nodes poses issues for a // particular widget class, it can set this property to ensure // that its template is always re-built from a string _skipNodeCache: false, // _earlyTemplatedStartup: Boolean // A fallback to preserve the 1.0 - 1.3 behavior of children in // templates having their startup called before the parent widget // fires postCreate. Defaults to 'false', causing child widgets to // have their .startup() called immediately before a parent widget // .startup(), but always after the parent .postCreate(). Set to // 'true' to re-enable to previous, arguably broken, behavior. _earlyTemplatedStartup: false, /*===== // _attachPoints: [private] String[] // List of widget attribute names associated with data-dojo-attach-point=... in the // template, ex: ["containerNode", "labelNode"] _attachPoints: [], =====*/ /*===== // _attachEvents: [private] Handle[] // List of connections associated with data-dojo-attach-event=... in the // template _attachEvents: [], =====*/ constructor: function(){ this._attachPoints = []; this._attachEvents = []; }, _stringRepl: function(tmpl){ // summary: // Does substitution of ${foo} type properties in template string // tags: // private var className = this.declaredClass, _this = this; // Cache contains a string because we need to do property replacement // do the property replacement return string.substitute(tmpl, this, function(value, key){ if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); } if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide if(value == null){ return ""; } // Substitution keys beginning with ! will skip the transform step, // in case a user wishes to insert unescaped markup, e.g. ${!foo} return key.charAt(0) == "!" ? value : // Safer substitution, see heading "Attribute values" in // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method? }, this); }, buildRendering: function(){ // summary: // Construct the UI for this widget from a template, setting this.domNode. // tags: // protected if(!this.templateString){ this.templateString = cache(this.templatePath, {sanitize: true}); } // Lookup cached version of template, and download to cache if it // isn't there already. Returns either a DomNode or a string, depending on // whether or not the template contains ${foo} replacement parameters. var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache); var node; if(lang.isString(cached)){ node = domConstruct.toDom(this._stringRepl(cached)); if(node.nodeType != 1){ // Flag common problems such as templates with multiple top level nodes (nodeType == 11) throw new Error("Invalid template: " + cached); } }else{ // if it's a node, all we have to do is clone it node = cached.cloneNode(true); } this.domNode = node; // Call down to _Widget.buildRendering() to get base classes assigned // TODO: change the baseClass assignment to _setBaseClassAttr this.inherited(arguments); // recurse through the node, looking for, and attaching to, our // attachment points and events, which should be defined on the template node. this._attachTemplateNodes(node, function(n,p){ return n.getAttribute(p); }); this._beforeFillContent(); // hook for _WidgetsInTemplateMixin this._fillContent(this.srcNodeRef); }, _beforeFillContent: function(){ }, _fillContent: function(/*DomNode*/ source){ // summary: // Relocate source contents to templated container node. // this.containerNode must be able to receive children, or exceptions will be thrown. // tags: // protected var dest = this.containerNode; if(source && dest){ while(source.hasChildNodes()){ dest.appendChild(source.firstChild); } } }, _attachTemplateNodes: function(rootNode, getAttrFunc){ // summary: // Iterate through the template and attach functions and nodes accordingly. // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point // etc. for those widgets. // description: // Map widget properties and functions to the handlers specified in // the dom node and it's descendants. This function iterates over all // nodes and looks for these properties: // * dojoAttachPoint/data-dojo-attach-point // * dojoAttachEvent/data-dojo-attach-event // rootNode: DomNode|Widget[] // the node to search for properties. All children will be searched. // getAttrFunc: Function // a function which will be used to obtain property for a given // DomNode/Widget // tags: // private var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); var x = lang.isArray(rootNode) ? 0 : -1; for(; x= 0 && prevColFocusIdx != colFocusIdx){ html.toggleClass(this._findHeaderCells()[prevColFocusIdx],this.focusClass,false); } html.toggleClass(colHeaderNode,this.focusClass, true); this._colHeadNode = colHeaderNode; this._colHeadFocusIdx = colFocusIdx; this._scrollHeader(this._colHeadFocusIdx); }, scrollIntoView: function(){ var info = (this.cell ? this._scrollInfo(this.cell) : null); if(!info || !info.s){ return null; } var rt = this.grid.scroller.findScrollTop(this.rowIndex); // place cell within horizontal view if(info.n && info.sr){ if(info.n.offsetLeft + info.n.offsetWidth > info.sr.l + info.sr.w){ info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w; }else if(info.n.offsetLeft < info.sr.l){ info.s.scrollLeft = info.n.offsetLeft; } } // place cell within vertical view if(info.r && info.sr){ if(rt + info.r.offsetHeight > info.sr.t + info.sr.h){ this.grid.setScrollTop(rt + info.r.offsetHeight - info.sr.h); }else if(rt < info.sr.t){ this.grid.setScrollTop(rt); } } return info.s.scrollLeft; }, _scrollInfo: function(cell, domNode){ if(cell){ var cl = cell, sbn = cl.view.scrollboxNode, sbnr = { w: sbn.clientWidth, l: sbn.scrollLeft, t: sbn.scrollTop, h: sbn.clientHeight }, rn = cl.view.getRowNode(this.rowIndex); return { c: cl, s: sbn, sr: sbnr, n: (domNode ? domNode : cell.getNode(this.rowIndex)), r: rn }; } return null; }, _scrollHeader: function(currentIdx){ var info = null; if(this._colHeadNode){ var cell = this.grid.getCell(currentIdx); if(!cell){ return; } info = this._scrollInfo(cell, cell.getNode(0)); } if(info && info.s && info.sr && info.n){ // scroll horizontally as needed. var scroll = info.sr.l + info.sr.w; if(info.n.offsetLeft + info.n.offsetWidth > scroll){ info.s.scrollLeft = info.n.offsetLeft + info.n.offsetWidth - info.sr.w; }else if(info.n.offsetLeft < info.sr.l){ info.s.scrollLeft = info.n.offsetLeft; }else if(has("ie") <= 7 && cell && cell.view.headerNode){ // Trac 7158: scroll dojoxGridHeader for IE7 and lower cell.view.headerNode.scrollLeft = info.s.scrollLeft; } } }, _isHeaderHidden: function(){ // summary: // determine if the grid headers are hidden // relies on documented technique of setting .dojoxGridHeader { display:none; } // returns: Boolean // true if headers are hidden // false if headers are not hidden var curView = this.focusView; if (!curView){ // find one so we can determine if headers are hidden // there is no focusView after adding items to empty grid (test_data_grid_empty.html) for (var i = 0, cView; (cView = this.grid.views.views[i]); i++) { if(cView.headerNode ){ curView=cView; break; } } } return (curView && html.getComputedStyle(curView.headerNode).display == "none"); }, colSizeAdjust: function (e, colIdx, delta){ // adjust the column specified by colIdx by the specified delta px var headers = this._findHeaderCells(); var view = this.focusView; if (!view) { for (var i = 0, cView; (cView = this.grid.views.views[i]); i++) { // find first view with a tableMap in order to work with empty grid if(cView.header.tableMap.map ){ view=cView; break; } } } var curHeader = headers[colIdx]; if (!view || (colIdx == headers.length-1 && colIdx === 0)){ return; // can't adjust single col. grid } view.content.baseDecorateEvent(e); // need to adjust event with header cell info since focus is no longer on header cell e.cellNode = curHeader; //this.findCellTarget(e.target, e.rowNode); e.cellIndex = view.content.getCellNodeIndex(e.cellNode); e.cell = (e.cellIndex >= 0 ? this.grid.getCell(e.cellIndex) : null); if (view.header.canResize(e)){ var deltaObj = { l: delta }; var drag = view.header.colResizeSetup(e,false); view.header.doResizeColumn(drag, null, deltaObj); view.update(); } }, styleRow: function(inRow){ return; }, setFocusIndex: function(inRowIndex, inCellIndex){ // summary: // focuses the given grid cell // inRowIndex: int // grid row index // inCellIndex: int // grid cell index this.setFocusCell(this.grid.getCell(inCellIndex), inRowIndex); }, setFocusCell: function(inCell, inRowIndex){ // summary: // focuses the given grid cell // inCell: object // grid cell object // inRowIndex: int // grid row index if(inCell && !this.isFocusCell(inCell, inRowIndex)){ this.tabbingOut = false; if (this._colHeadNode){ this.blurHeader(); } this._colHeadNode = this._colHeadFocusIdx = null; this.focusGridView(); this._focusifyCellNode(false); this.cell = inCell; this.rowIndex = inRowIndex; this._focusifyCellNode(true); } // even if this cell isFocusCell, the document focus may need to be rejiggered // call opera on delay to prevent keypress from altering focus if(has("opera")){ setTimeout(lang.hitch(this.grid, 'onCellFocus', this.cell, this.rowIndex), 1); }else{ this.grid.onCellFocus(this.cell, this.rowIndex); } }, next: function(){ // summary: // focus next grid cell if(this.cell){ var row=this.rowIndex, col=this.cell.index+1, cc=this.grid.layout.cellCount-1, rc=this.grid.rowCount-1; if(col > cc){ col = 0; row++; } if(row > rc){ col = cc; row = rc; } if(this.grid.edit.isEditing()){ //when editing, only navigate to editable cells var nextCell = this.grid.getCell(col); if (!this.isLastFocusCell() && (!nextCell.editable || this.grid.canEdit && !this.grid.canEdit(nextCell, row))){ this.cell=nextCell; this.rowIndex=row; this.next(); return; } } this.setFocusIndex(row, col); } }, previous: function(){ // summary: // focus previous grid cell if(this.cell){ var row=(this.rowIndex || 0), col=(this.cell.index || 0) - 1; if(col < 0){ col = this.grid.layout.cellCount-1; row--; } if(row < 0){ row = 0; col = 0; } if(this.grid.edit.isEditing()){ //when editing, only navigate to editable cells var prevCell = this.grid.getCell(col); if (!this.isFirstFocusCell() && !prevCell.editable){ this.cell=prevCell; this.rowIndex=row; this.previous(); return; } } this.setFocusIndex(row, col); } }, move: function(inRowDelta, inColDelta) { // summary: // focus grid cell or simulate focus to column header based on position relative to current focus // inRowDelta: int // vertical distance from current focus // inColDelta: int // horizontal distance from current focus var colDir = inColDelta < 0 ? -1 : 1; // Handle column headers. if(this.isNavHeader()){ var headers = this._findHeaderCells(); var savedIdx = currentIdx = array.indexOf(headers, this._colHeadNode); currentIdx += inColDelta; while(currentIdx >=0 && currentIdx < headers.length && headers[currentIdx].style.display == "none"){ // skip over hidden column headers currentIdx += colDir; } if((currentIdx >= 0) && (currentIdx < headers.length)){ this._setActiveColHeader(headers[currentIdx],currentIdx, savedIdx); } }else{ if(this.cell){ // Handle grid proper. var sc = this.grid.scroller, r = this.rowIndex, rc = this.grid.rowCount-1, row = Math.min(rc, Math.max(0, r+inRowDelta)); if(inRowDelta){ if(inRowDelta>0){ if(row > sc.getLastPageRow(sc.page)){ //need to load additional data, let scroller do that this.grid.setScrollTop(this.grid.scrollTop+sc.findScrollTop(row)-sc.findScrollTop(r)); } }else if(inRowDelta<0){ if(row <= sc.getPageRow(sc.page)){ //need to load additional data, let scroller do that this.grid.setScrollTop(this.grid.scrollTop-sc.findScrollTop(r)-sc.findScrollTop(row)); } } } var cc = this.grid.layout.cellCount-1, i = this.cell.index, col = Math.min(cc, Math.max(0, i+inColDelta)); var cell = this.grid.getCell(col); while(col>=0 && col < cc && cell && cell.hidden === true){ // skip hidden cells col += colDir; cell = this.grid.getCell(col); } if (!cell || cell.hidden === true){ // don't change col if would move to hidden col = i; } //skip hidden row|cell var n = cell.getNode(row); if(!n && inRowDelta){ if((row + inRowDelta) >= 0 && (row + inRowDelta) <= rc){ this.move(inRowDelta > 0 ? ++inRowDelta : --inRowDelta, inColDelta); } return; }else if((!n || html.style(n, "display") === "none") && inColDelta){ if((col + inColDelta) >= 0 && (col + inColDelta) <= cc){ this.move(inRowDelta, inColDelta > 0 ? ++inColDelta : --inColDelta); } return; } this.setFocusIndex(row, col); if(inRowDelta){ this.grid.updateRow(r); } } } }, previousKey: function(e){ if(this.grid.edit.isEditing()){ event.stop(e); this.previous(); }else if(!this.isNavHeader() && !this._isHeaderHidden()) { this.grid.domNode.focus(); // will call doFocus and set focus into header. event.stop(e); }else{ this.tabOut(this.grid.domNode); if (this._colHeadFocusIdx != null) { // clear grid header focus html.toggleClass(this._findHeaderCells()[this._colHeadFocusIdx], this.focusClass, false); this._colHeadFocusIdx = null; } this._focusifyCellNode(false); } }, nextKey: function(e) { var isEmpty = (this.grid.rowCount === 0); if(e.target === this.grid.domNode && this._colHeadFocusIdx == null){ this.focusHeader(); event.stop(e); }else if(this.isNavHeader()){ // if tabbing from col header, then go to grid proper. this.blurHeader(); if(!this.findAndFocusGridCell()){ this.tabOut(this.grid.lastFocusNode); } this._colHeadNode = this._colHeadFocusIdx= null; }else if(this.grid.edit.isEditing()){ event.stop(e); this.next(); }else{ this.tabOut(this.grid.lastFocusNode); } }, tabOut: function(inFocusNode){ this.tabbingOut = true; inFocusNode.focus(); }, focusGridView: function(){ util.fire(this.focusView, "focus"); }, focusGrid: function(inSkipFocusCell){ this.focusGridView(); this._focusifyCellNode(true); }, findAndFocusGridCell: function(){ // summary: // find the first focusable grid cell // returns: Boolean // true if focus was set to a cell // false if no cell found to set focus onto var didFocus = true; var isEmpty = (this.grid.rowCount === 0); // If grid is empty this.grid.rowCount == 0 if (this.isNoFocusCell() && !isEmpty){ var cellIdx = 0; var cell = this.grid.getCell(cellIdx); if (cell.hidden) { // if first cell isn't visible, use _colHeadFocusIdx // could also use a while loop to find first visible cell - not sure that is worth it cellIdx = this.isNavHeader() ? this._colHeadFocusIdx : 0; } this.setFocusIndex(0, cellIdx); } else if (this.cell && !isEmpty){ if (this.focusView && !this.focusView.rowNodes[this.rowIndex]){ // if rowNode for current index is undefined (likely as a result of a sort and because of #7304) // scroll to that row this.grid.scrollToRow(this.rowIndex); } this.focusGrid(); }else { didFocus = false; } this._colHeadNode = this._colHeadFocusIdx= null; return didFocus; }, focusHeader: function(){ var headerNodes = this._findHeaderCells(); var saveColHeadFocusIdx = this._colHeadFocusIdx; if (this._isHeaderHidden()){ // grid header is hidden, focus a cell this.findAndFocusGridCell(); } else if (!this._colHeadFocusIdx) { if (this.isNoFocusCell()) { this._colHeadFocusIdx = 0; } else { this._colHeadFocusIdx = this.cell.index; } } this._colHeadNode = headerNodes[this._colHeadFocusIdx]; while(this._colHeadNode && this._colHeadFocusIdx >=0 && this._colHeadFocusIdx < headerNodes.length && this._colHeadNode.style.display == "none"){ // skip over hidden column headers this._colHeadFocusIdx++; this._colHeadNode = headerNodes[this._colHeadFocusIdx]; } if(this._colHeadNode && this._colHeadNode.style.display != "none"){ // Column header cells know longer receive actual focus. So, for keyboard invocation of // contextMenu to work, the contextMenu must be bound to the grid.domNode rather than the viewsHeaderNode. // unbind the contextmenu from the viewsHeaderNode and to the grid when header cells are active. Reset // the binding back to the viewsHeaderNode when header cells are no longer acive (in blurHeader) #10483 if (this.headerMenu && this._contextMenuBindNode != this.grid.domNode){ this.headerMenu.unBindDomNode(this.grid.viewsHeaderNode); this.headerMenu.bindDomNode(this.grid.domNode); this._contextMenuBindNode = this.grid.domNode; } this._setActiveColHeader(this._colHeadNode, this._colHeadFocusIdx, saveColHeadFocusIdx); this._scrollHeader(this._colHeadFocusIdx); this._focusifyCellNode(false); }else { // all col head nodes are hidden - focus the grid this.findAndFocusGridCell(); } }, blurHeader: function(){ html.removeClass(this._colHeadNode, this.focusClass); html.removeAttr(this.grid.domNode,"aria-activedescendant"); // reset contextMenu onto viewsHeaderNode so right mouse on header will invoke (see focusHeader) if (this.headerMenu && this._contextMenuBindNode == this.grid.domNode) { var viewsHeader = this.grid.viewsHeaderNode; this.headerMenu.unBindDomNode(this.grid.domNode); this.headerMenu.bindDomNode(viewsHeader); this._contextMenuBindNode = viewsHeader; } }, doFocus: function(e){ // trap focus only for grid dom node if(e && e.target != e.currentTarget){ event.stop(e); return; } // don't change focus if clicking on scroller bar if(this._clickFocus){ return; } // do not focus for scrolling if grid is about to blur if(!this.tabbingOut){ this.focusHeader(); } this.tabbingOut = false; event.stop(e); }, doBlur: function(e){ event.stop(e); // FF2 }, doContextMenu: function(e){ //stop contextMenu event if no header Menu to prevent default/browser contextMenu if (!this.headerMenu){ event.stop(e); } }, doLastNodeFocus: function(e){ if (this.tabbingOut){ this._focusifyCellNode(false); }else if(this.grid.rowCount >0){ if (this.isNoFocusCell()){ this.setFocusIndex(0,0); } this._focusifyCellNode(true); }else { this.focusHeader(); } this.tabbingOut = false; event.stop(e); // FF2 }, doLastNodeBlur: function(e){ event.stop(e); // FF2 }, doColHeaderFocus: function(e){ this._setActiveColHeader(e.target,html.attr(e.target, "idx"),this._colHeadFocusIdx); this._scrollHeader(this.getHeaderIndex()); event.stop(e); }, doColHeaderBlur: function(e){ html.toggleClass(e.target, this.focusClass, false); }, _mouseDown: function(e){ // a flag indicating grid is being focused by clicking this._clickFocus = dojo.some(this.grid.views.views, function(v){ return v.scrollboxNode === e.target; }); }, _mouseUp: function(e){ this._clickFocus = false; } }); }); }, 'dojox/grid/EnhancedGrid':function(){ define("dojox/grid/EnhancedGrid", [ "dojo/_base/kernel", "../main", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/sniff", "dojo/dom", "dojo/dom-geometry", "dojo/i18n", "./DataGrid", "./DataSelection", "./enhanced/_PluginManager", "./enhanced/plugins/_SelectionPreserver",//default loaded plugin "dojo/i18n!./enhanced/nls/EnhancedGrid" ], function(dojo, dojox, declare, lang, array, has, dom, domGeometry, i18n, DataGrid, DataSelection, _PluginManager, _SelectionPreserver){ dojo.experimental("dojox.grid.EnhancedGrid"); var EnhancedGrid = declare("dojox.grid.EnhancedGrid", DataGrid, { // summary: // Provides enhanced features based on DataGrid // // description: // EnhancedGrid features are implemented as plugins that could be loaded on demand. // Explicit dojo.require() is needed to use these feature plugins. // // example: // A quick sample to use EnhancedGrid features: // // Step 1. Load EnhancedGrid and required features // | // // Step 2. Use EnhancedGrid // - Via HTML markup // |
// | ... // |
// // - Or via JavaScript // | // // // Plugin Support // [Note: Plugin support is still experimental] // // You can either customize the default plugins or add new ones, more details please see // - dojox.grid.enhanced._PluginManager // - dojox.grid.enhanced._Plugin // - dojox.grid.enhanced.plugins.* //plugins: Object // Plugin properties, e.g. {nestedSorting: true, dnd: true, ...} plugins: null, //pluginMgr: Object // Singleton plugin manager pluginMgr: null, //_pluginMgrClass: Object // Default plugin manager class _pluginMgrClass: _PluginManager, postMixInProperties: function(){ //load nls bundle this._nls = i18n.getLocalization("dojox.grid.enhanced", "EnhancedGrid", this.lang); this.inherited(arguments); }, postCreate: function(){ //create plugin manager this.pluginMgr = new this._pluginMgrClass(this); this.pluginMgr.preInit(); this.inherited(arguments); this.pluginMgr.postInit(); }, plugin: function(/*String*/name){ // summary: // An easier way for getting a plugin, e.g. grid.plugin('dnd') return this.pluginMgr.getPlugin(name); }, startup: function(){ this.inherited(arguments); this.pluginMgr.startup(); }, createSelection: function(){ this.selection = new dojox.grid.enhanced.DataSelection(this); }, canSort: function(colIndex, field){ // summary: // Overwritten return true; }, doKeyEvent: function(e){ // summary: // Overwritten, see _Grid.doKeyEvent() try{ var view = this.focus.focusView; view.content.decorateEvent(e); if(!e.cell){ view.header.decorateEvent(e); } }catch(e){} this.inherited(arguments); }, doApplyCellEdit: function(inValue, inRowIndex, inAttrName){ // summary: // Overwritten, see DataGrid.doApplyCellEdit() if(!inAttrName){ this.invalidated[inRowIndex] = true; return; } this.inherited(arguments); }, mixin: function(target, source){ var props = {}; for(var p in source){ if(p == '_inherited' || p == 'declaredClass' || p == 'constructor' || source['privates'] && source['privates'][p]){ continue; } props[p] = source[p]; } lang.mixin(target, props); }, _copyAttr: function(idx, attr){ // summary: // Overwritten, see DataGrid._copyAttr() // Fix cell TAB navigation for single click editing if(!attr){ return; } return this.inherited(arguments); }, _getHeaderHeight: function(){ // summary: // Overwritten, see _Grid._getHeaderHeight() // Should include borders/margins of this.viewsHeaderNode this.inherited(arguments); return domGeometry.getMarginBox(this.viewsHeaderNode).h; }, _fetch: function(start, isRender){ // summary: // Overwritten, see DataGrid._fetch() if(this.items){ return this.inherited(arguments); } start = start || 0; if(this.store && !this._pending_requests[start]){ if(!this._isLoaded && !this._isLoading){ this._isLoading = true; this.showMessage(this.loadingMessage); } this._pending_requests[start] = true; try{ var req = { start: start, count: this.rowsPerPage, query: this.query, sort: this.getSortProps(), queryOptions: this.queryOptions, isRender: isRender, onBegin: lang.hitch(this, "_onFetchBegin"), onComplete: lang.hitch(this, "_onFetchComplete"), onError: lang.hitch(this, "_onFetchError") }; this._storeLayerFetch(req); }catch(e){ this._onFetchError(e, {start: start, count: this.rowsPerPage}); } } return 0; }, _storeLayerFetch: function(req){ // summary: // Extracted fetch specifically for store layer use this.store.fetch(req); }, getCellByField: function(field){ return array.filter(this.layout.cells, function(cell){ return cell.field == field; })[0]; }, onMouseUp: function(e){ }, createView: function(){ // summary // Overwrite: rewrite getCellX of view.header var view = this.inherited(arguments); if(has("mozilla")){ var ascendDom = function(inNode, inWhile){ for(var n = inNode; n && inWhile(n); n = n.parentNode){} return n; };//copied from dojox.grid._Builder var makeNotTagName = function(inTagName){ var name = inTagName.toUpperCase(); return function(node){ return node.tagName != name; }; };//copied from dojox.grid._Builder var func = view.header.getCellX; view.header.getCellX = function(e){ var x = func.call(view.header, e); var n = ascendDom(e.target, makeNotTagName("th")); if(n && n !== e.target && dom.isDescendant(e.target, n)){ x += n.firstChild.offsetLeft; } return x; }; } return view; }, destroy: function(){ // summary: // Destroy all resources delete this._nls; this.pluginMgr.destroy(); this.inherited(arguments); } }); declare("dojox.grid.enhanced.DataSelection", DataSelection, { constructor: function(grid){ if(grid.keepSelection){ if(this.preserver){ this.preserver.destroy(); } this.preserver = new _SelectionPreserver(this); } }, _range: function(inFrom, inTo){ this.grid._selectingRange = true; this.inherited(arguments); this.grid._selectingRange = false; this.onChanged(); }, deselectAll: function(inItemOrIndex){ this.grid._selectingRange = true; this.inherited(arguments); this.grid._selectingRange = false; this.onChanged(); } }); EnhancedGrid.markupFactory = function(props, node, ctor, cellFunc){ return dojox.grid._Grid.markupFactory(props, node, ctor, lang.partial(DataGrid.cell_markupFactory, cellFunc)); }; EnhancedGrid.registerPlugin = function(clazz, props){ _PluginManager.registerPlugin(clazz, props); }; return EnhancedGrid; }); }, 'dijit/_CssStateMixin':function(){ define("dijit/_CssStateMixin", [ "dojo/touch", "dojo/_base/array", // array.forEach array.map "dojo/_base/declare", // declare "dojo/dom-class", // domClass.toggle "dojo/_base/lang", // lang.hitch "dojo/_base/window" // win.body ], function(touch, array, declare, domClass, lang, win){ // module: // dijit/_CssStateMixin // summary: // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus // state changes, and also higher-level state changes such becoming disabled or selected. return declare("dijit._CssStateMixin", [], { // summary: // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus // state changes, and also higher-level state changes such becoming disabled or selected. // // description: // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically // maintain CSS classes on the widget root node (this.domNode) depending on hover, // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it. // // It also sets CSS like dijitButtonDisabled based on widget semantic state. // // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons // within the widget). // cssStateNodes: [protected] Object // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus //. // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names // (like "dijitUpArrowButton"). Example: // | { // | "upArrowButton": "dijitUpArrowButton", // | "downArrowButton": "dijitDownArrowButton" // | } // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it // is hovered, etc. cssStateNodes: {}, // hovering: [readonly] Boolean // True if cursor is over this widget hovering: false, // active: [readonly] Boolean // True if mouse was pressed while over this widget, and hasn't been released yet active: false, _applyAttributes: function(){ // This code would typically be in postCreate(), but putting in _applyAttributes() for // performance: so the class changes happen before DOM is inserted into the document. // Change back to postCreate() in 2.0. See #11635. this.inherited(arguments); // Automatically monitor mouse events (essentially :hover and :active) on this.domNode array.forEach(["onmouseenter", "onmouseleave", touch.press], function(e){ this.connect(this.domNode, e, "_cssMouseEvent"); }, this); // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){ this.watch(attr, lang.hitch(this, "_setStateClass")); }, this); // Events on sub nodes within the widget for(var ap in this.cssStateNodes){ this._trackMouseState(this[ap], this.cssStateNodes[ap]); } // Set state initially; there's probably no hover/active/focus state but widget might be // disabled/readonly/checked/selected so we want to set CSS classes for those conditions. this._setStateClass(); }, _cssMouseEvent: function(/*Event*/ event){ // summary: // Sets hovering and active properties depending on mouse state, // which triggers _setStateClass() to set appropriate CSS classes for this.domNode. if(!this.disabled){ switch(event.type){ case "mouseenter": case "mouseover": // generated on non-IE browsers even though we connected to mouseenter this._set("hovering", true); this._set("active", this._mouseDown); break; case "mouseleave": case "mouseout": // generated on non-IE browsers even though we connected to mouseleave this._set("hovering", false); this._set("active", false); break; case "mousedown": case "touchpress": this._set("active", true); this._mouseDown = true; // Set a global event to handle mouseup, so it fires properly // even if the cursor leaves this.domNode before the mouse up event. // Alternately could set active=false on mouseout. var mouseUpConnector = this.connect(win.body(), touch.release, function(){ this._mouseDown = false; this._set("active", false); this.disconnect(mouseUpConnector); }); break; } } }, _setStateClass: function(){ // summary: // Update the visual state of the widget by setting the css classes on this.domNode // (or this.stateNode if defined) by combining this.baseClass with // various suffixes that represent the current widget state(s). // // description: // In the case where a widget has multiple // states, it sets the class based on all possible // combinations. For example, an invalid form widget that is being hovered // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover". // // The widget may have one or more of the following states, determined // by this.state, this.checked, this.valid, and this.selected: // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true // - Selected - ex: currently selected tab will have this.selected==true // // In addition, it may have one or more of the following states, // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused): // - Disabled - if the widget is disabled // - Active - if the mouse (or space/enter key?) is being pressed down // - Focused - if the widget has focus // - Hover - if the mouse is over the widget // Compute new set of classes var newStateClasses = this.baseClass.split(" "); function multiply(modifier){ newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier); } if(!this.isLeftToRight()){ // For RTL mode we need to set an addition class like dijitTextBoxRtl. multiply("Rtl"); } var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : ""); if(this.checked){ multiply(checkedState); } if(this.state){ multiply(this.state); } if(this.selected){ multiply("Selected"); } if(this.disabled){ multiply("Disabled"); }else if(this.readOnly){ multiply("ReadOnly"); }else{ if(this.active){ multiply("Active"); }else if(this.hovering){ multiply("Hover"); } } if(this.focused){ multiply("Focused"); } // Remove old state classes and add new ones. // For performance concerns we only write into domNode.className once. var tn = this.stateNode || this.domNode, classHash = {}; // set of all classes (state and otherwise) for node array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; }); if("_stateClasses" in this){ array.forEach(this._stateClasses, function(c){ delete classHash[c]; }); } array.forEach(newStateClasses, function(c){ classHash[c] = true; }); var newClasses = []; for(var c in classHash){ newClasses.push(c); } tn.className = newClasses.join(" "); this._stateClasses = newStateClasses; }, _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){ // summary: // Track mouse/focus events on specified node and set CSS class on that node to indicate // current state. Usually not called directly, but via cssStateNodes attribute. // description: // Given class=foo, will set the following CSS class on the node // - fooActive: if the user is currently pressing down the mouse button while over the node // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button // - fooFocus: if the node is focused // // Note that it won't set any classes if the widget is disabled. // node: DomNode // Should be a sub-node of the widget, not the top node (this.domNode), since the top node // is handled specially and automatically just by mixing in this class. // clazz: String // CSS class name (ex: dijitSliderUpArrow). // Current state of node (initially false) // NB: setting specifically to false because domClass.toggle() needs true boolean as third arg var hovering=false, active=false, focused=false; var self = this, cn = lang.hitch(this, "connect", node); function setClass(){ var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly); domClass.toggle(node, clazz+"Hover", hovering && !active && !disabled); domClass.toggle(node, clazz+"Active", active && !disabled); domClass.toggle(node, clazz+"Focused", focused && !disabled); } // Mouse cn("onmouseenter", function(){ hovering = true; setClass(); }); cn("onmouseleave", function(){ hovering = false; active = false; setClass(); }); cn(touch.press, function(){ active = true; setClass(); }); cn(touch.release, function(){ active = false; setClass(); }); // Focus cn("onfocus", function(){ focused = true; setClass(); }); cn("onblur", function(){ focused = false; setClass(); }); // Just in case widget is enabled/disabled while it has focus/hover/active state. // Maybe this is overkill. this.watch("disabled", setClass); this.watch("readOnly", setClass); } }); }); }, 'dojo/currency':function(){ define("dojo/currency", ["./_base/kernel", "./_base/lang", "./_base/array", "./number", "./i18n", "./i18n!./cldr/nls/currency", "./cldr/monetary"], function(dojo, lang, darray, dnumber, i18n, nlsCurrency, cldrMonetary) { // module: // dojo/currency // summary: // TODOC lang.getObject("currency", true, dojo); /*===== dojo.currency = { // summary: localized formatting and parsing routines for currencies // // description: extends dojo.number to provide culturally-appropriate formatting of values // in various world currencies, including use of a currency symbol. The currencies are specified // by a three-letter international symbol in all uppercase, and support for the currencies is // provided by the data in `dojo.cldr`. The scripts generating dojo.cldr specify which // currency support is included. A fixed number of decimal places is determined based // on the currency type and is not determined by the 'pattern' argument. The fractional // portion is optional, by default, and variable length decimals are not supported. } =====*/ dojo.currency._mixInDefaults = function(options){ options = options || {}; options.type = "currency"; // Get locale-dependent currency data, like the symbol var bundle = i18n.getLocalization("dojo.cldr", "currency", options.locale) || {}; // Mixin locale-independent currency data, like # of places var iso = options.currency; var data = cldrMonetary.getData(iso); darray.forEach(["displayName","symbol","group","decimal"], function(prop){ data[prop] = bundle[iso+"_"+prop]; }); data.fractional = [true, false]; // Mixin with provided options return lang.mixin(data, options); }; /*===== dojo.declare("dojo.currency.__FormatOptions", [dojo.number.__FormatOptions], { // type: String? // Should not be set. Value is assumed to be "currency". // symbol: String? // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr` // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found. // currency: String? // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD". // For use with dojo.currency only. // places: Number? // number of decimal places to show. Default is defined based on which currency is used. type: "", symbol: "", currency: "", places: "" }); =====*/ dojo.currency.format = function(/*Number*/value, /*dojo.currency.__FormatOptions?*/options){ // summary: // Format a Number as a currency, using locale-specific settings // // description: // Create a string from a Number using a known, localized pattern. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements) // appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr) // as well as the appropriate symbols and delimiters and number of decimal places. // // value: // the number to be formatted. return dnumber.format(value, dojo.currency._mixInDefaults(options)); }; dojo.currency.regexp = function(/*dojo.number.__RegexpOptions?*/options){ // // summary: // Builds the regular needed to parse a currency value // // description: // Returns regular expression with positive and negative match, group and decimal separators // Note: the options.places default, the number of decimal places to accept, is defined by the currency type. return dnumber.regexp(dojo.currency._mixInDefaults(options)); // String }; /*===== dojo.declare("dojo.currency.__ParseOptions", [dojo.number.__ParseOptions], { // type: String? // Should not be set. Value is assumed to be currency. // currency: String? // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD". // For use with dojo.currency only. // symbol: String? // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr` // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found. // places: Number? // fixed number of decimal places to accept. The default is determined based on which currency is used. // fractional: Boolean?|Array? // Whether to include the fractional portion, where the number of decimal places are implied by the currency // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional. // By default for currencies, it the fractional portion is optional. type: "", currency: "", symbol: "", places: "", fractional: "" }); =====*/ dojo.currency.parse = function(/*String*/expression, /*dojo.currency.__ParseOptions?*/options){ // // summary: // Convert a properly formatted currency string to a primitive Number, // using locale-specific settings. // // description: // Create a Number from a string using a known, localized pattern. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // are chosen appropriate to the locale, as well as the appropriate symbols and delimiters // and number of decimal places. // // expression: A string representation of a currency value return dnumber.parse(expression, dojo.currency._mixInDefaults(options)); }; return dojo.currency; }); }, 'dijit/_editor/html':function(){ define("dijit/_editor/html", [ "dojo/_base/lang", // lang.isString "dojo/_base/sniff", // has("ie") ".." // for exporting symbols to dijit._editor (remove for 2.0) ], function(lang, has, dijit){ // module: // dijit/_editor/html // summary: // Utility functions used by editor lang.getObject("_editor", true, dijit); dijit._editor.escapeXml=function(/*String*/str, /*Boolean?*/noSingleQuotes){ // summary: // Adds escape sequences for special characters in XML: &<>"' // Optionally skips escapes for single quotes str = str.replace(/&/gm, "&").replace(//gm, ">").replace(/"/gm, """); if(!noSingleQuotes){ str = str.replace(/'/gm, "'"); } return str; // string }; dijit._editor.getNodeHtml=function(/* DomNode */node){ var output; switch(node.nodeType){ case 1: //element node var lName = node.nodeName.toLowerCase(); if(!lName || lName.charAt(0) == "/"){ // IE does some strange things with malformed HTML input, like // treating a close tag
without an open tag , as // a new tag with tagName of /span. Corrupts output HTML, remove // them. Other browsers don't prefix tags that way, so will // never show up. return ""; } output = '<' + lName; //store the list of attributes and sort it to have the //attributes appear in the dictionary order var attrarray = []; var attr; if(has("ie") && node.outerHTML){ var s = node.outerHTML; s = s.substr(0, s.indexOf('>')) .replace(/(['"])[^"']*\1/g, ''); //to make the following regexp safe var reg = /(\b\w+)\s?=/g; var m, key; while((m = reg.exec(s))){ key = m[1]; if(key.substr(0,3) != '_dj'){ if(key == 'src' || key == 'href'){ if(node.getAttribute('_djrealurl')){ attrarray.push([key,node.getAttribute('_djrealurl')]); continue; } } var val, match; switch(key){ case 'style': val = node.style.cssText.toLowerCase(); break; case 'class': val = node.className; break; case 'width': if(lName === "img"){ // This somehow gets lost on IE for IMG tags and the like // and we have to find it in outerHTML, known IE oddity. match=/width=(\S+)/i.exec(s); if(match){ val = match[1]; } break; } case 'height': if(lName === "img"){ // This somehow gets lost on IE for IMG tags and the like // and we have to find it in outerHTML, known IE oddity. match=/height=(\S+)/i.exec(s); if(match){ val = match[1]; } break; } default: val = node.getAttribute(key); } if(val != null){ attrarray.push([key, val.toString()]); } } } }else{ var i = 0; while((attr = node.attributes[i++])){ //ignore all attributes starting with _dj which are //internal temporary attributes used by the editor var n = attr.name; if(n.substr(0,3) != '_dj' /*&& (attr.specified == undefined || attr.specified)*/){ var v = attr.value; if(n == 'src' || n == 'href'){ if(node.getAttribute('_djrealurl')){ v = node.getAttribute('_djrealurl'); } } attrarray.push([n,v]); } } } attrarray.sort(function(a,b){ return a[0] < b[0] ? -1 : (a[0] == b[0] ? 0 : 1); }); var j = 0; while((attr = attrarray[j++])){ output += ' ' + attr[0] + '="' + (lang.isString(attr[1]) ? dijit._editor.escapeXml(attr[1], true) : attr[1]) + '"'; } if(lName === "script"){ // Browsers handle script tags differently in how you get content, // but innerHTML always seems to work, so insert its content that way // Yes, it's bad to allow script tags in the editor code, but some people // seem to want to do it, so we need to at least return them right. // other plugins/filters can strip them. output += '>' + node.innerHTML +''; }else{ if(node.childNodes.length){ output += '>' + dijit._editor.getChildrenHtml(node)+''; }else{ switch(lName){ case 'br': case 'hr': case 'img': case 'input': case 'base': case 'meta': case 'area': case 'basefont': // These should all be singly closed output += ' />'; break; default: // Assume XML style separate closure for everything else. output += '>'; } } } break; case 4: // cdata case 3: // text // FIXME: output = dijit._editor.escapeXml(node.nodeValue, true); break; case 8: //comment // FIXME: output = ''; break; default: output = ""; } return output; }; dijit._editor.getChildrenHtml = function(/* DomNode */dom){ // summary: // Returns the html content of a DomNode and children var out = ""; if(!dom){ return out; } var nodes = dom["childNodes"] || dom; //IE issue. //If we have an actual node we can check parent relationships on for IE, //We should check, as IE sometimes builds invalid DOMS. If no parent, we can't check //And should just process it and hope for the best. var checkParent = !has("ie") || nodes !== dom; var node, i = 0; while((node = nodes[i++])){ //IE is broken. DOMs are supposed to be a tree. But in the case of malformed HTML, IE generates a graph //meaning one node ends up with multiple references (multiple parents). This is totally wrong and invalid, but //such is what it is. We have to keep track and check for this because otherise the source output HTML will have dups. //No other browser generates a graph. Leave it to IE to break a fundamental DOM rule. So, we check the parent if we can //If we can't, nothing more we can do other than walk it. if(!checkParent || node.parentNode == dom){ out += dijit._editor.getNodeHtml(node); } } return out; // String }; return dijit._editor; }); }, 'dijit/place':function(){ define("dijit/place", [ "dojo/_base/array", // array.forEach array.map array.some "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position "dojo/dom-style", // domStyle.getComputedStyle "dojo/_base/kernel", // kernel.deprecated "dojo/_base/window", // win.body "dojo/window", // winUtils.getBox "." // dijit (defining dijit.place to match API doc) ], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){ // module: // dijit/place // summary: // Code to place a popup relative to another node function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){ // summary: // Given a list of spots to put node, put it at the first spot where it fits, // of if it doesn't fit anywhere then the place with the least overflow // choices: Array // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} } // Above example says to put the top-left corner of the node at (10,20) // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size) // for things like tooltip, they are displayed differently (and have different dimensions) // based on their orientation relative to the parent. This adjusts the popup based on orientation. // It also passes in the available size for the popup, which is useful for tooltips to // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing // how much the popup had to be modified to fit into the available space. This is used to determine // what the best placement is. // aroundNodeCoords: Object // Size of aroundNode, ex: {w: 200, h: 50} // get {x: 10, y: 10, w: 100, h:100} type obj representing position of // viewport over document var view = winUtils.getBox(); // This won't work if the node is inside a
, // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong // and also it might get cutoff) if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){ win.body().appendChild(node); } var best = null; array.some(choices, function(choice){ var corner = choice.corner; var pos = choice.pos; var overflow = 0; // calculate amount of space available given specified position of node var spaceAvailable = { w: { 'L': view.l + view.w - pos.x, 'R': pos.x - view.l, 'M': view.w }[corner.charAt(1)], h: { 'T': view.t + view.h - pos.y, 'B': pos.y - view.t, 'M': view.h }[corner.charAt(0)] }; // configure node to be displayed in given position relative to button // (need to do this in order to get an accurate size for the node, because // a tooltip's size changes based on position, due to triangle) if(layoutNode){ var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords); overflow = typeof res == "undefined" ? 0 : res; } // get node's size var style = node.style; var oldDisplay = style.display; var oldVis = style.visibility; if(style.display == "none"){ style.visibility = "hidden"; style.display = ""; } var mb = domGeometry. getMarginBox(node); style.display = oldDisplay; style.visibility = oldVis; // coordinates and size of node with specified corner placed at pos, // and clipped by viewport var startXpos = { 'L': pos.x, 'R': pos.x - mb.w, 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (mb.w >> 1)) - mb.w) // M orientation is more flexible }[corner.charAt(1)], startYpos = { 'T': pos.y, 'B': pos.y - mb.h, 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (mb.h >> 1)) - mb.h) }[corner.charAt(0)], startX = Math.max(view.l, startXpos), startY = Math.max(view.t, startYpos), endX = Math.min(view.l + view.w, startXpos + mb.w), endY = Math.min(view.t + view.h, startYpos + mb.h), width = endX - startX, height = endY - startY; overflow += (mb.w - width) + (mb.h - height); if(best == null || overflow < best.overflow){ best = { corner: corner, aroundCorner: choice.aroundCorner, x: startX, y: startY, w: width, h: height, overflow: overflow, spaceAvailable: spaceAvailable }; } return !overflow; }); // In case the best position is not the last one we checked, need to call // layoutNode() again. if(best.overflow && layoutNode){ layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords); } // And then position the node. Do this last, after the layoutNode() above // has sized the node, due to browser quirks when the viewport is scrolled // (specifically that a Tooltip will shrink to fit as though the window was // scrolled to the left). // // In RTL mode, set style.right rather than style.left so in the common case, // window resizes move the popup along with the aroundNode. var l = domGeometry.isBodyLtr(), s = node.style; s.top = best.y + "px"; s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px"; s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left return best; } /*===== dijit.place.__Position = function(){ // x: Integer // horizontal coordinate in pixels, relative to document body // y: Integer // vertical coordinate in pixels, relative to document body this.x = x; this.y = y; }; =====*/ /*===== dijit.place.__Rectangle = function(){ // x: Integer // horizontal offset in pixels, relative to document body // y: Integer // vertical offset in pixels, relative to document body // w: Integer // width in pixels. Can also be specified as "width" for backwards-compatibility. // h: Integer // height in pixels. Can also be specified as "height" from backwards-compatibility. this.x = x; this.y = y; this.w = w; this.h = h; }; =====*/ return (dijit.place = { // summary: // Code to place a DOMNode relative to another DOMNode. // Load using require(["dijit/place"], function(place){ ... }). at: function(node, pos, corners, padding){ // summary: // Positions one of the node's corners at specified position // such that node is fully visible in viewport. // description: // NOTE: node is assumed to be absolutely or relatively positioned. // node: DOMNode // The node to position // pos: dijit.place.__Position // Object like {x: 10, y: 20} // corners: String[] // Array of Strings representing order to try corners in, like ["TR", "BL"]. // Possible values are: // * "BL" - bottom left // * "BR" - bottom right // * "TL" - top left // * "TR" - top right // padding: dijit.place.__Position? // optional param to set padding, to put some buffer around the element you want to position. // example: // Try to place node's top right corner at (10,20). // If that makes node go (partially) off screen, then try placing // bottom left corner at (10,20). // | place(node, {x: 10, y: 20}, ["TR", "BL"]) var choices = array.map(corners, function(corner){ var c = { corner: corner, pos: {x:pos.x,y:pos.y} }; if(padding){ c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x; c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y; } return c; }); return _place(node, choices); }, around: function( /*DomNode*/ node, /*DomNode || dijit.place.__Rectangle*/ anchor, /*String[]*/ positions, /*Boolean*/ leftToRight, /*Function?*/ layoutNode){ // summary: // Position node adjacent or kitty-corner to anchor // such that it's fully visible in viewport. // // description: // Place node such that corner of node touches a corner of // aroundNode, and that node is fully visible. // // anchor: // Either a DOMNode or a __Rectangle (object with x, y, width, height). // // positions: // Ordered list of positions to try matching up. // * before: places drop down to the left of the anchor node/widget, or to the right in the case // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down // with the top of the anchor, or the bottom of the drop down with bottom of the anchor. // * after: places drop down to the right of the anchor node/widget, or to the left in the case // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down // with the top of the anchor, or the bottom of the drop down with bottom of the anchor. // * before-centered: centers drop down to the left of the anchor node/widget, or to the right // in the case of RTL scripts like Hebrew and Arabic // * after-centered: centers drop down to the right of the anchor node/widget, or to the left // in the case of RTL scripts like Hebrew and Arabic // * above-centered: drop down is centered above anchor node // * above: drop down goes above anchor node, left sides aligned // * above-alt: drop down goes above anchor node, right sides aligned // * below-centered: drop down is centered above anchor node // * below: drop down goes below anchor node // * below-alt: drop down goes below anchor node, right sides aligned // // layoutNode: Function(node, aroundNodeCorner, nodeCorner) // For things like tooltip, they are displayed differently (and have different dimensions) // based on their orientation relative to the parent. This adjusts the popup based on orientation. // // leftToRight: // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below" // positions slightly. // // example: // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'}); // This will try to position node such that node's top-left corner is at the same position // as the bottom left corner of the aroundNode (ie, put node below // aroundNode, with left edges aligned). If that fails it will try to put // the bottom-right corner of node where the top right corner of aroundNode is // (ie, put node above aroundNode, with right edges aligned) // // if around is a DOMNode (or DOMNode id), convert to coordinates var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor) ? domGeometry.position(anchor, true) : anchor; // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars) if(anchor.parentNode){ // ignore nodes between position:relative and position:absolute var sawPosAbsolute = domStyle.getComputedStyle(anchor).position == "absolute"; var parent = anchor.parentNode; while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance var parentPos = domGeometry.position(parent, true), pcs = domStyle.getComputedStyle(parent); if(/relative|absolute/.test(pcs.position)){ sawPosAbsolute = false; } if(!sawPosAbsolute && /hidden|auto|scroll/.test(pcs.overflow)){ var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h); var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w); aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x); aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y); aroundNodePos.h = bottomYCoord - aroundNodePos.y; aroundNodePos.w = rightXCoord - aroundNodePos.x; } if(pcs.position == "absolute"){ sawPosAbsolute = true; } parent = parent.parentNode; } } var x = aroundNodePos.x, y = aroundNodePos.y, width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width), height = "h" in aroundNodePos ? aroundNodePos.h : (kernel.deprecated("place.around: dijit.place.__Rectangle: { x:"+x+", y:"+y+", height:"+aroundNodePos.height+", width:"+width+" } has been deprecated. Please use { x:"+x+", y:"+y+", h:"+aroundNodePos.height+", w:"+width+" }", "", "2.0"), aroundNodePos.h = aroundNodePos.height); // Convert positions arguments into choices argument for _place() var choices = []; function push(aroundCorner, corner){ choices.push({ aroundCorner: aroundCorner, corner: corner, pos: { x: { 'L': x, 'R': x + width, 'M': x + (width >> 1) }[aroundCorner.charAt(1)], y: { 'T': y, 'B': y + height, 'M': y + (height >> 1) }[aroundCorner.charAt(0)] } }) } array.forEach(positions, function(pos){ var ltr = leftToRight; switch(pos){ case "above-centered": push("TM", "BM"); break; case "below-centered": push("BM", "TM"); break; case "after-centered": ltr = !ltr; // fall through case "before-centered": push(ltr ? "ML" : "MR", ltr ? "MR" : "ML"); break; case "after": ltr = !ltr; // fall through case "before": push(ltr ? "TL" : "TR", ltr ? "TR" : "TL"); push(ltr ? "BL" : "BR", ltr ? "BR" : "BL"); break; case "below-alt": ltr = !ltr; // fall through case "below": // first try to align left borders, next try to align right borders (or reverse for RTL mode) push(ltr ? "BL" : "BR", ltr ? "TL" : "TR"); push(ltr ? "BR" : "BL", ltr ? "TR" : "TL"); break; case "above-alt": ltr = !ltr; // fall through case "above": // first try to align left borders, next try to align right borders (or reverse for RTL mode) push(ltr ? "TL" : "TR", ltr ? "BL" : "BR"); push(ltr ? "TR" : "TL", ltr ? "BR" : "BL"); break; default: // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}. // Not meant to be used directly. push(pos.aroundCorner, pos.corner); } }); var position = _place(node, choices, layoutNode, {w: width, h: height}); position.aroundNodePos = aroundNodePos; return position; } }); }); }, 'dijit/_HasDropDown':function(){ define("dijit/_HasDropDown", [ "dojo/_base/declare", // declare "dojo/_base/Deferred", "dojo/_base/event", // event.stop "dojo/dom", // dom.isDescendant "dojo/dom-attr", // domAttr.set "dojo/dom-class", // domClass.add domClass.contains domClass.remove "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position "dojo/dom-style", // domStyle.set "dojo/has", "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE "dojo/_base/lang", // lang.hitch lang.isFunction "dojo/touch", "dojo/_base/window", // win.doc "dojo/window", // winUtils.getBox "./registry", // registry.byNode() "./focus", "./popup", "./_FocusMixin" ], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, touch, win, winUtils, registry, focus, popup, _FocusMixin){ /*===== var _FocusMixin = dijit._FocusMixin; =====*/ // module: // dijit/_HasDropDown // summary: // Mixin for widgets that need drop down ability. return declare("dijit._HasDropDown", _FocusMixin, { // summary: // Mixin for widgets that need drop down ability. // _buttonNode: [protected] DomNode // The button/icon/node to click to display the drop down. // Can be set via a data-dojo-attach-point assignment. // If missing, then either focusNode or domNode (if focusNode is also missing) will be used. _buttonNode: null, // _arrowWrapperNode: [protected] DomNode // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending // on where the drop down is set to be positioned. // Can be set via a data-dojo-attach-point assignment. // If missing, then _buttonNode will be used. _arrowWrapperNode: null, // _popupStateNode: [protected] DomNode // The node to set the popupActive class on. // Can be set via a data-dojo-attach-point assignment. // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used. _popupStateNode: null, // _aroundNode: [protected] DomNode // The node to display the popup around. // Can be set via a data-dojo-attach-point assignment. // If missing, then domNode will be used. _aroundNode: null, // dropDown: [protected] Widget // The widget to display as a popup. This widget *must* be // defined before the startup function is called. dropDown: null, // autoWidth: [protected] Boolean // Set to true to make the drop down at least as wide as this // widget. Set to false if the drop down should just be its // default width autoWidth: true, // forceWidth: [protected] Boolean // Set to true to make the drop down exactly as wide as this // widget. Overrides autoWidth. forceWidth: false, // maxHeight: [protected] Integer // The max height for our dropdown. // Any dropdown taller than this will have scrollbars. // Set to 0 for no max height, or -1 to limit height to available space in viewport maxHeight: 0, // dropDownPosition: [const] String[] // This variable controls the position of the drop down. // It's an array of strings with the following values: // // * before: places drop down to the left of the target node/widget, or to the right in // the case of RTL scripts like Hebrew and Arabic // * after: places drop down to the right of the target node/widget, or to the left in // the case of RTL scripts like Hebrew and Arabic // * above: drop down goes above target node // * below: drop down goes below target node // // The list is positions is tried, in order, until a position is found where the drop down fits // within the viewport. // dropDownPosition: ["below","above"], // _stopClickEvents: Boolean // When set to false, the click events will not be stopped, in // case you want to use them in your subwidget _stopClickEvents: true, _onDropDownMouseDown: function(/*Event*/ e){ // summary: // Callback when the user mousedown's on the arrow icon if(this.disabled || this.readOnly){ return; } // Prevent default to stop things like text selection, but don't stop propogation, so that: // 1. TimeTextBox etc. can focusthe on mousedown // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress) // 3. user defined onMouseDown handler fires e.preventDefault(); this._docHandler = this.connect(win.doc, touch.release, "_onDropDownMouseUp"); this.toggleDropDown(); }, _onDropDownMouseUp: function(/*Event?*/ e){ // summary: // Callback when the user lifts their mouse after mouse down on the arrow icon. // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our // drop down widget. If the event is missing, then we are not // a mouseup event. // // This is useful for the common mouse movement pattern // with native browser and doesn't // generate touchstart/touchend events. Pretend we just got a mouse down / mouse up. // The if(has("ios") is necessary since IE and desktop safari get spurious onclick events // when there are nested tables (specifically, clicking on a table that holds a dijit.form.Select, // but not on the Select itself, causes an onclick event on the Select) this._onDropDownMouseDown(e); this._onDropDownMouseUp(e); } // The drop down was already opened on mousedown/keydown; just need to call stopEvent(). if(this._stopClickEvents){ event.stop(e); } }, buildRendering: function(){ this.inherited(arguments); this._buttonNode = this._buttonNode || this.focusNode || this.domNode; this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode; // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow // based on where drop down will normally appear var defaultPos = { "after" : this.isLeftToRight() ? "Right" : "Left", "before" : this.isLeftToRight() ? "Left" : "Right", "above" : "Up", "below" : "Down", "left" : "Left", "right" : "Right" }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down"; domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton"); }, postCreate: function(){ // summary: // set up nodes and connect our mouse and keypress events this.inherited(arguments); this.connect(this._buttonNode, touch.press, "_onDropDownMouseDown"); this.connect(this._buttonNode, "onclick", "_onDropDownClick"); this.connect(this.focusNode, "onkeypress", "_onKey"); this.connect(this.focusNode, "onkeyup", "_onKeyUp"); }, destroy: function(){ if(this.dropDown){ // Destroy the drop down, unless it's already been destroyed. This can happen because // the drop down is a direct child of even though it's logically my child. if(!this.dropDown._destroyed){ this.dropDown.destroyRecursive(); } delete this.dropDown; } this.inherited(arguments); }, _onKey: function(/*Event*/ e){ // summary: // Callback when the user presses a key while focused on the button node if(this.disabled || this.readOnly){ return; } var d = this.dropDown, target = e.target; if(d && this._opened && d.handleKey){ if(d.handleKey(e) === false){ /* false return code means that the drop down handled the key */ event.stop(e); return; } } if(d && this._opened && e.charOrCode == keys.ESCAPE){ this.closeDropDown(); event.stop(e); }else if(!this._opened && (e.charOrCode == keys.DOWN_ARROW || ( (e.charOrCode == keys.ENTER || e.charOrCode == " ") && //ignore enter and space if the event is for a text input ((target.tagName || "").toLowerCase() !== 'input' || (target.type && target.type.toLowerCase() !== 'text'))))){ // Toggle the drop down, but wait until keyup so that the drop down doesn't // get a stray keyup event, or in the case of key-repeat (because user held // down key for too long), stray keydown events this._toggleOnKeyUp = true; event.stop(e); } }, _onKeyUp: function(){ if(this._toggleOnKeyUp){ delete this._toggleOnKeyUp; this.toggleDropDown(); var d = this.dropDown; // drop down may not exist until toggleDropDown() call if(d && d.focus){ setTimeout(lang.hitch(d, "focus"), 1); } } }, _onBlur: function(){ // summary: // Called magically when focus has shifted away from this widget and it's dropdown // Don't focus on button if the user has explicitly focused on something else (happens // when user clicks another control causing the current popup to close).. // But if focus is inside of the drop down then reset focus to me, because IE doesn't like // it when you display:none a node with focus. var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode); this.closeDropDown(focusMe); this.inherited(arguments); }, isLoaded: function(){ // summary: // Returns true if the dropdown exists and it's data is loaded. This can // be overridden in order to force a call to loadDropDown(). // tags: // protected return true; }, loadDropDown: function(/*Function*/ loadCallback){ // summary: // Creates the drop down if it doesn't exist, loads the data // if there's an href and it hasn't been loaded yet, and then calls // the given callback. // tags: // protected // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback? loadCallback(); }, loadAndOpenDropDown: function(){ // summary: // Creates the drop down if it doesn't exist, loads the data // if there's an href and it hasn't been loaded yet, and // then opens the drop down. This is basically a callback when the // user presses the down arrow button to open the drop down. // returns: Deferred // Deferred for the drop down widget that // fires when drop down is created and loaded // tags: // protected var d = new Deferred(), afterLoad = lang.hitch(this, function(){ this.openDropDown(); d.resolve(this.dropDown); }); if(!this.isLoaded()){ this.loadDropDown(afterLoad); }else{ afterLoad(); } return d; }, toggleDropDown: function(){ // summary: // Callback when the user presses the down arrow button or presses // the down arrow key to open/close the drop down. // Toggle the drop-down widget; if it is up, close it, if not, open it // tags: // protected if(this.disabled || this.readOnly){ return; } if(!this._opened){ this.loadAndOpenDropDown(); }else{ this.closeDropDown(); } }, openDropDown: function(){ // summary: // Opens the dropdown for this widget. To be called only when this.dropDown // has been created and is ready to display (ie, it's data is loaded). // returns: // return value of dijit.popup.open() // tags: // protected var dropDown = this.dropDown, ddNode = dropDown.domNode, aroundNode = this._aroundNode || this.domNode, self = this; // Prepare our popup's height and honor maxHeight if it exists. // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(), // ie, dependent on how much space is available (BK) if(!this._preparedNode){ this._preparedNode = true; // Check if we have explicitly set width and height on the dropdown widget dom node if(ddNode.style.width){ this._explicitDDWidth = true; } if(ddNode.style.height){ this._explicitDDHeight = true; } } // Code for resizing dropdown (height limitation, or increasing width to match my width) if(this.maxHeight || this.forceWidth || this.autoWidth){ var myStyle = { display: "", visibility: "hidden" }; if(!this._explicitDDWidth){ myStyle.width = ""; } if(!this._explicitDDHeight){ myStyle.height = ""; } domStyle.set(ddNode, myStyle); // Figure out maximum height allowed (if there is a height restriction) var maxHeight = this.maxHeight; if(maxHeight == -1){ // limit height to space available in viewport either above or below my domNode // (whichever side has more room) var viewport = winUtils.getBox(), position = domGeometry.position(aroundNode, false); maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h))); } // Attach dropDown to DOM and make make visibility:hidden rather than display:none // so we call startup() and also get the size popup.moveOffScreen(dropDown); if(dropDown.startup && !dropDown._started){ dropDown.startup(); // this has to be done after being added to the DOM } // Get size of drop down, and determine if vertical scroll bar needed var mb = domGeometry.getMarginSize(ddNode); var overHeight = (maxHeight && mb.h > maxHeight); domStyle.set(ddNode, { overflowX: "hidden", overflowY: overHeight ? "auto" : "hidden" }); if(overHeight){ mb.h = maxHeight; if("w" in mb){ mb.w += 16; // room for vertical scrollbar } }else{ delete mb.h; } // Adjust dropdown width to match or be larger than my width if(this.forceWidth){ mb.w = aroundNode.offsetWidth; }else if(this.autoWidth){ mb.w = Math.max(mb.w, aroundNode.offsetWidth); }else{ delete mb.w; } // And finally, resize the dropdown to calculated height and width if(lang.isFunction(dropDown.resize)){ dropDown.resize(mb); }else{ domGeometry.setMarginBox(ddNode, mb); } } var retVal = popup.open({ parent: this, popup: dropDown, around: aroundNode, orient: this.dropDownPosition, onExecute: function(){ self.closeDropDown(true); }, onCancel: function(){ self.closeDropDown(true); }, onClose: function(){ domAttr.set(self._popupStateNode, "popupActive", false); domClass.remove(self._popupStateNode, "dijitHasDropDownOpen"); self._opened = false; } }); domAttr.set(this._popupStateNode, "popupActive", "true"); domClass.add(self._popupStateNode, "dijitHasDropDownOpen"); this._opened=true; // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown return retVal; }, closeDropDown: function(/*Boolean*/ focus){ // summary: // Closes the drop down on this widget // focus: // If true, refocuses the button widget // tags: // protected if(this._opened){ if(focus){ this.focus(); } popup.close(this.dropDown); this._opened = false; } } }); }); }, 'dijit/_editor/plugins/EnterKeyHandling':function(){ define("dijit/_editor/plugins/EnterKeyHandling", [ "dojo/_base/declare", // declare "dojo/dom-construct", // domConstruct.destroy domConstruct.place "dojo/_base/event", // event.stop "dojo/keys", // keys.ENTER "dojo/_base/lang", "dojo/_base/sniff", // has("ie") has("mozilla") has("webkit") "dojo/_base/window", // win.global win.withGlobal "dojo/window", // winUtils.scrollIntoView "../_Plugin", "../RichText", "../range", "../selection" ], function(declare, domConstruct, event, keys, lang, has, win, winUtils, _Plugin, RichText, rangeapi, selectionapi){ /*===== var _Plugin = dijit._editor._Plugin; =====*/ // module: // dijit/_editor/plugins/EnterKeyHandling // summary: // This plugin tries to make all browsers behave consistently with regard to // how ENTER behaves in the editor window. It traps the ENTER key and alters // the way DOM is constructed in certain cases to try to commonize the generated // DOM and behaviors across browsers. return declare("dijit._editor.plugins.EnterKeyHandling", _Plugin, { // summary: // This plugin tries to make all browsers behave consistently with regard to // how ENTER behaves in the editor window. It traps the ENTER key and alters // the way DOM is constructed in certain cases to try to commonize the generated // DOM and behaviors across browsers. // // description: // This plugin has three modes: // // * blockNodeForEnter=BR // * blockNodeForEnter=DIV // * blockNodeForEnter=P // // In blockNodeForEnter=P, the ENTER key starts a new // paragraph, and shift-ENTER starts a new line in the current paragraph. // For example, the input: // // | first paragraph // | second line of first paragraph // | second paragraph // // will generate: // // |

// | first paragraph // |
// | second line of first paragraph // |

// |

// | second paragraph // |

// // In BR and DIV mode, the ENTER key conceptually goes to a new line in the // current paragraph, and users conceptually create a new paragraph by pressing ENTER twice. // For example, if the user enters text into an editor like this: // // | one // | two // | three // | // | four // | five // | six // // It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates: // // BR: // | one
// | two
// | three
// |
// | four
// | five
// | six
// // DIV: // |
one
// |
two
// |
three
// |
 
// |
four
// |
five
// |
six
// blockNodeForEnter: String // This property decides the behavior of Enter key. It can be either P, // DIV, BR, or empty (which means disable this feature). Anything else // will trigger errors. The default is 'BR' // // See class description for more details. blockNodeForEnter: 'BR', constructor: function(args){ if(args){ if("blockNodeForEnter" in args){ args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase(); } lang.mixin(this,args); } }, setEditor: function(editor){ // Overrides _Plugin.setEditor(). if(this.editor === editor){ return; } this.editor = editor; if(this.blockNodeForEnter == 'BR'){ // While Moz has a mode tht mostly works, it's still a little different, // So, try to just have a common mode and be consistent. Which means // we need to enable customUndo, if not already enabled. this.editor.customUndo = true; editor.onLoadDeferred.then(lang.hitch(this,function(d){ this.connect(editor.document, "onkeypress", function(e){ if(e.charOrCode == keys.ENTER){ // Just do it manually. The handleEnterKey has a shift mode that // Always acts like
, so just use it. var ne = lang.mixin({},e); ne.shiftKey = true; if(!this.handleEnterKey(ne)){ event.stop(e); } } }); if(has("ie") == 9){ this.connect(editor.document, "onpaste", function(e){ setTimeout(dojo.hitch(this, function(){ // Use the old range/selection code to kick IE 9 into updating // its range by moving it back, then forward, one 'character'. var r = this.editor.document.selection.createRange(); r.move('character',-1); r.select(); r.move('character',1); r.select(); }),0); }); } return d; })); }else if(this.blockNodeForEnter){ // add enter key handler // FIXME: need to port to the new event code!! var h = lang.hitch(this,this.handleEnterKey); editor.addKeyHandler(13, 0, 0, h); //enter editor.addKeyHandler(13, 0, 1, h); //shift+enter this.connect(this.editor,'onKeyPressed','onKeyPressed'); } }, onKeyPressed: function(){ // summary: // Handler for keypress events. // tags: // private if(this._checkListLater){ if(win.withGlobal(this.editor.window, 'isCollapsed', dijit)){ var liparent=win.withGlobal(this.editor.window, 'getAncestorElement', selectionapi, ['LI']); if(!liparent){ // circulate the undo detection code by calling RichText::execCommand directly RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter); // set the innerHTML of the new block node var block = win.withGlobal(this.editor.window, 'getAncestorElement', selectionapi, [this.blockNodeForEnter]); if(block){ block.innerHTML=this.bogusHtmlContent; if(has("ie")){ // move to the start by moving backwards one char var r = this.editor.document.selection.createRange(); r.move('character',-1); r.select(); } }else{ console.error('onKeyPressed: Cannot find the new block node'); // FIXME } }else{ if(has("mozilla")){ if(liparent.parentNode.parentNode.nodeName == 'LI'){ liparent=liparent.parentNode.parentNode; } } var fc=liparent.firstChild; if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){ liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'),fc); var newrange = rangeapi.create(this.editor.window); newrange.setStart(liparent.firstChild,0); var selection = rangeapi.getSelection(this.editor.window, true); selection.removeAllRanges(); selection.addRange(newrange); } } } this._checkListLater = false; } if(this._pressedEnterInBlock){ // the new created is the original current P, so we have previousSibling below if(this._pressedEnterInBlock.previousSibling){ this.removeTrailingBr(this._pressedEnterInBlock.previousSibling); } delete this._pressedEnterInBlock; } }, // bogusHtmlContent: [private] String // HTML to stick into a new empty block bogusHtmlContent: ' ', //   // blockNodes: [private] Regex // Regex for testing if a given tag is a block level (display:block) tag blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/, handleEnterKey: function(e){ // summary: // Handler for enter key events when blockNodeForEnter is DIV or P. // description: // Manually handle enter key event to make the behavior consistent across // all supported browsers. See class description for details. // tags: // private var selection, range, newrange, startNode, endNode, brNode, doc=this.editor.document,br,rs,txt; if(e.shiftKey){ // shift+enter always generates
var parent = win.withGlobal(this.editor.window, "getParentElement", selectionapi); var header = rangeapi.getAncestor(parent,this.blockNodes); if(header){ if(header.tagName == 'LI'){ return true; // let browser handle } selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); if(!range.collapsed){ range.deleteContents(); selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); } if(rangeapi.atBeginningOfContainer(header, range.startContainer, range.startOffset)){ br=doc.createElement('br'); newrange = rangeapi.create(this.editor.window); header.insertBefore(br,header.firstChild); newrange.setStartAfter(br); selection.removeAllRanges(); selection.addRange(newrange); }else if(rangeapi.atEndOfContainer(header, range.startContainer, range.startOffset)){ newrange = rangeapi.create(this.editor.window); br=doc.createElement('br'); header.appendChild(br); header.appendChild(doc.createTextNode('\xA0')); newrange.setStart(header.lastChild,0); selection.removeAllRanges(); selection.addRange(newrange); }else{ rs = range.startContainer; if(rs && rs.nodeType == 3){ // Text node, we have to split it. txt = rs.nodeValue; win.withGlobal(this.editor.window, function(){ startNode = doc.createTextNode(txt.substring(0, range.startOffset)); endNode = doc.createTextNode(txt.substring(range.startOffset)); brNode = doc.createElement("br"); if(endNode.nodeValue == "" && has("webkit")){ endNode = doc.createTextNode('\xA0') } domConstruct.place(startNode, rs, "after"); domConstruct.place(brNode, startNode, "after"); domConstruct.place(endNode, brNode, "after"); domConstruct.destroy(rs); newrange = rangeapi.create(); newrange.setStart(endNode,0); selection.removeAllRanges(); selection.addRange(newrange); }); return false; } return true; // let browser handle } }else{ selection = rangeapi.getSelection(this.editor.window); if(selection.rangeCount){ range = selection.getRangeAt(0); if(range && range.startContainer){ if(!range.collapsed){ range.deleteContents(); selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); } rs = range.startContainer; if(rs && rs.nodeType == 3){ // Text node, we have to split it. win.withGlobal(this.editor.window, lang.hitch(this, function(){ var endEmpty = false; var offset = range.startOffset; if(rs.length < offset){ //We are not splitting the right node, try to locate the correct one ret = this._adjustNodeAndOffset(rs, offset); rs = ret.node; offset = ret.offset; } txt = rs.nodeValue; startNode = doc.createTextNode(txt.substring(0, offset)); endNode = doc.createTextNode(txt.substring(offset)); brNode = doc.createElement("br"); if(!endNode.length){ endNode = doc.createTextNode('\xA0'); endEmpty = true; } if(startNode.length){ domConstruct.place(startNode, rs, "after"); }else{ startNode = rs; } domConstruct.place(brNode, startNode, "after"); domConstruct.place(endNode, brNode, "after"); domConstruct.destroy(rs); newrange = rangeapi.create(); newrange.setStart(endNode,0); newrange.setEnd(endNode, endNode.length); selection.removeAllRanges(); selection.addRange(newrange); if(endEmpty && !has("webkit")){ selectionapi.remove(); }else{ selectionapi.collapse(true); } })); }else{ var targetNode; if(range.startOffset >= 0){ targetNode = rs.childNodes[range.startOffset]; } win.withGlobal(this.editor.window, lang.hitch(this, function(){ var brNode = doc.createElement("br"); var endNode = doc.createTextNode('\xA0'); if(!targetNode){ rs.appendChild(brNode); rs.appendChild(endNode); }else{ domConstruct.place(brNode, targetNode, "before"); domConstruct.place(endNode, brNode, "after"); } newrange = rangeapi.create(win.global); newrange.setStart(endNode,0); newrange.setEnd(endNode, endNode.length); selection.removeAllRanges(); selection.addRange(newrange); selectionapi.collapse(true); })); } } }else{ // don't change this: do not call this.execCommand, as that may have other logic in subclass RichText.prototype.execCommand.call(this.editor, 'inserthtml', '
'); } } return false; } var _letBrowserHandle = true; // first remove selection selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); if(!range.collapsed){ range.deleteContents(); selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); } var block = rangeapi.getBlockAncestor(range.endContainer, null, this.editor.editNode); var blockNode = block.blockNode; // if this is under a LI or the parent of the blockNode is LI, just let browser to handle it if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){ if(has("mozilla")){ // press enter in middle of P may leave a trailing
, let's remove it later this._pressedEnterInBlock = blockNode; } // if this li only contains spaces, set the content to empty so the browser will outdent this item if(/^(\s| | |\xA0|]*\bclass=['"]Apple-style-span['"][^>]*>(\s| | |\xA0)<\/span>)?(
)?$/.test(blockNode.innerHTML)){ // empty LI node blockNode.innerHTML = ''; if(has("webkit")){ // WebKit tosses the range when innerHTML is reset newrange = rangeapi.create(this.editor.window); newrange.setStart(blockNode, 0); selection.removeAllRanges(); selection.addRange(newrange); } this._checkListLater = false; // nothing to check since the browser handles outdent } return true; } // text node directly under body, let's wrap them in a node if(!block.blockNode || block.blockNode===this.editor.editNode){ try{ RichText.prototype.execCommand.call(this.editor, 'formatblock',this.blockNodeForEnter); }catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ } // get the newly created block node // FIXME block = {blockNode:win.withGlobal(this.editor.window, "getAncestorElement", selectionapi, [this.blockNodeForEnter]), blockContainer: this.editor.editNode}; if(block.blockNode){ if(block.blockNode != this.editor.editNode && (!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){ this.removeTrailingBr(block.blockNode); return false; } }else{ // we shouldn't be here if formatblock worked block.blockNode = this.editor.editNode; } selection = rangeapi.getSelection(this.editor.window); range = selection.getRangeAt(0); } var newblock = doc.createElement(this.blockNodeForEnter); newblock.innerHTML=this.bogusHtmlContent; this.removeTrailingBr(block.blockNode); var endOffset = range.endOffset; var node = range.endContainer; if(node.length < endOffset){ //We are not checking the right node, try to locate the correct one var ret = this._adjustNodeAndOffset(node, endOffset); node = ret.node; endOffset = ret.offset; } if(rangeapi.atEndOfContainer(block.blockNode, node, endOffset)){ if(block.blockNode === block.blockContainer){ block.blockNode.appendChild(newblock); }else{ domConstruct.place(newblock, block.blockNode, "after"); } _letBrowserHandle = false; // lets move caret to the newly created block newrange = rangeapi.create(this.editor.window); newrange.setStart(newblock, 0); selection.removeAllRanges(); selection.addRange(newrange); if(this.editor.height){ winUtils.scrollIntoView(newblock); } }else if(rangeapi.atBeginningOfContainer(block.blockNode, range.startContainer, range.startOffset)){ domConstruct.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before"); if(newblock.nextSibling && this.editor.height){ // position input caret - mostly WebKit needs this newrange = rangeapi.create(this.editor.window); newrange.setStart(newblock.nextSibling, 0); selection.removeAllRanges(); selection.addRange(newrange); // browser does not scroll the caret position into view, do it manually winUtils.scrollIntoView(newblock.nextSibling); } _letBrowserHandle = false; }else{ //press enter in the middle of P/DIV/Whatever/ if(block.blockNode === block.blockContainer){ block.blockNode.appendChild(newblock); }else{ domConstruct.place(newblock, block.blockNode, "after"); } _letBrowserHandle = false; // Clone any block level styles. if(block.blockNode.style){ if(newblock.style){ if(block.blockNode.style.cssText){ newblock.style.cssText = block.blockNode.style.cssText; } } } // Okay, we probably have to split. rs = range.startContainer; var firstNodeMoved; if(rs && rs.nodeType == 3){ // Text node, we have to split it. var nodeToMove, tNode; endOffset = range.endOffset; if(rs.length < endOffset){ //We are not splitting the right node, try to locate the correct one ret = this._adjustNodeAndOffset(rs, endOffset); rs = ret.node; endOffset = ret.offset; } txt = rs.nodeValue; startNode = doc.createTextNode(txt.substring(0, endOffset)); endNode = doc.createTextNode(txt.substring(endOffset, txt.length)); // Place the split, then remove original nodes. domConstruct.place(startNode, rs, "before"); domConstruct.place(endNode, rs, "after"); domConstruct.destroy(rs); // Okay, we split the text. Now we need to see if we're // parented to the block element we're splitting and if // not, we have to split all the way up. Ugh. var parentC = startNode.parentNode; while(parentC !== block.blockNode){ var tg = parentC.tagName; var newTg = doc.createElement(tg); // Clone over any 'style' data. if(parentC.style){ if(newTg.style){ if(parentC.style.cssText){ newTg.style.cssText = parentC.style.cssText; } } } // If font also need to clone over any font data. if(parentC.tagName === "FONT"){ if(parentC.color){ newTg.color = parentC.color; } if(parentC.face){ newTg.face = parentC.face; } if(parentC.size){ // this check was necessary on IE newTg.size = parentC.size; } } nodeToMove = endNode; while(nodeToMove){ tNode = nodeToMove.nextSibling; newTg.appendChild(nodeToMove); nodeToMove = tNode; } domConstruct.place(newTg, parentC, "after"); startNode = parentC; endNode = newTg; parentC = parentC.parentNode; } // Lastly, move the split out tags to the new block. // as they should now be split properly. nodeToMove = endNode; if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){ // Non-blank text and non-text nodes need to clear out that blank space // before moving the contents. newblock.innerHTML = ""; } firstNodeMoved = nodeToMove; while(nodeToMove){ tNode = nodeToMove.nextSibling; newblock.appendChild(nodeToMove); nodeToMove = tNode; } } //lets move caret to the newly created block newrange = rangeapi.create(this.editor.window); var nodeForCursor; var innerMostFirstNodeMoved = firstNodeMoved; if(this.blockNodeForEnter !== 'BR'){ while(innerMostFirstNodeMoved){ nodeForCursor = innerMostFirstNodeMoved; tNode = innerMostFirstNodeMoved.firstChild; innerMostFirstNodeMoved = tNode; } if(nodeForCursor && nodeForCursor.parentNode){ newblock = nodeForCursor.parentNode; newrange.setStart(newblock, 0); selection.removeAllRanges(); selection.addRange(newrange); if(this.editor.height){ winUtils.scrollIntoView(newblock); } if(has("mozilla")){ // press enter in middle of P may leave a trailing
, let's remove it later this._pressedEnterInBlock = block.blockNode; } }else{ _letBrowserHandle = true; } }else{ newrange.setStart(newblock, 0); selection.removeAllRanges(); selection.addRange(newrange); if(this.editor.height){ winUtils.scrollIntoView(newblock); } if(has("mozilla")){ // press enter in middle of P may leave a trailing
, let's remove it later this._pressedEnterInBlock = block.blockNode; } } } return _letBrowserHandle; }, _adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){ // summary: // In the case there are multiple text nodes in a row the offset may not be within the node. If the offset is larger than the node length, it will attempt to find // the next text sibling until it locates the text node in which the offset refers to // node: // The node to check. // offset: // The position to find within the text node // tags: // private. while(node.length < offset && node.nextSibling && node.nextSibling.nodeType==3){ //Adjust the offset and node in the case of multiple text nodes in a row offset = offset - node.length; node = node.nextSibling; } return {"node": node, "offset": offset}; }, removeTrailingBr: function(container){ // summary: // If last child of container is a
, then remove it. // tags: // private var para = /P|DIV|LI/i.test(container.tagName) ? container : selectionapi.getParentOfType(container,['P','DIV','LI']); if(!para){ return; } if(para.lastChild){ if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) || para.lastChild.tagName=='BR'){ domConstruct.destroy(para.lastChild); } } if(!para.childNodes.length){ para.innerHTML=this.bogusHtmlContent; } } }); }); }, 'dojo/dnd/Selector':function(){ define("dojo/dnd/Selector", ["../main", "./common", "./Container"], function(dojo) { // module: // dojo/dnd/Selector // summary: // TODOC /* Container item states: "" - an item is not selected "Selected" - an item is selected "Anchor" - an item is selected, and is an anchor for a "shift" selection */ /*===== dojo.declare("dojo.dnd.__SelectorArgs", [dojo.dnd.__ContainerArgs], { // singular: Boolean // allows selection of only one element, if true singular: false, // autoSync: Boolean // autosynchronizes the source with its list of DnD nodes, autoSync: false }); =====*/ dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, { // summary: // a Selector object, which knows how to select its children /*===== // selection: Set // The set of id's that are currently selected, such that this.selection[id] == 1 // if the node w/that id is selected. Can iterate over selected node's id's like: // | for(var id in this.selection) selection: {}, =====*/ constructor: function(node, params){ // summary: // constructor of the Selector // node: Node||String // node or node's id to build the selector on // params: dojo.dnd.__SelectorArgs? // a dictionary of parameters if(!params){ params = {}; } this.singular = params.singular; this.autoSync = params.autoSync; // class-specific variables this.selection = {}; this.anchor = null; this.simpleSelection = false; // set up events this.events.push( dojo.connect(this.node, "onmousedown", this, "onMouseDown"), dojo.connect(this.node, "onmouseup", this, "onMouseUp")); }, // object attributes (for markup) singular: false, // is singular property // methods getSelectedNodes: function(){ // summary: // returns a list (an array) of selected nodes var t = new dojo.NodeList(); var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } t.push(dojo.byId(i)); } return t; // NodeList }, selectNone: function(){ // summary: // unselects all items return this._removeSelection()._removeAnchor(); // self }, selectAll: function(){ // summary: // selects all items this.forInItems(function(data, id){ this._addItemClass(dojo.byId(id), "Selected"); this.selection[id] = 1; }, this); return this._removeAnchor(); // self }, deleteSelectedNodes: function(){ // summary: // deletes all selected items var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } var n = dojo.byId(i); this.delItem(i); dojo.destroy(n); } this.anchor = null; this.selection = {}; return this; // self }, forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){ // summary: // iterates over selected items; // see `dojo.dnd.Container.forInItems()` for details o = o || dojo.global; var s = this.selection, e = dojo.dnd._empty; for(var i in s){ if(i in e){ continue; } f.call(o, this.getItem(i), i, this); } }, sync: function(){ // summary: // sync up the node list with the data map dojo.dnd.Selector.superclass.sync.call(this); // fix the anchor if(this.anchor){ if(!this.getItem(this.anchor.id)){ this.anchor = null; } } // fix the selection var t = [], e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } if(!this.getItem(i)){ t.push(i); } } dojo.forEach(t, function(i){ delete this.selection[i]; }, this); return this; // self }, insertNodes: function(addSelected, data, before, anchor){ // summary: // inserts new data items (see `dojo.dnd.Container.insertNodes()` method for details) // addSelected: Boolean // all new nodes will be added to selected items, if true, no selection change otherwise // data: Array // a list of data items, which should be processed by the creator function // before: Boolean // insert before the anchor, if true, and after the anchor otherwise // anchor: Node // the anchor node to be used as a point of insertion var oldCreator = this._normalizedCreator; this._normalizedCreator = function(item, hint){ var t = oldCreator.call(this, item, hint); if(addSelected){ if(!this.anchor){ this.anchor = t.node; this._removeItemClass(t.node, "Selected"); this._addItemClass(this.anchor, "Anchor"); }else if(this.anchor != t.node){ this._removeItemClass(t.node, "Anchor"); this._addItemClass(t.node, "Selected"); } this.selection[t.node.id] = 1; }else{ this._removeItemClass(t.node, "Selected"); this._removeItemClass(t.node, "Anchor"); } return t; }; dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor); this._normalizedCreator = oldCreator; return this; // self }, destroy: function(){ // summary: // prepares the object to be garbage-collected dojo.dnd.Selector.superclass.destroy.call(this); this.selection = this.anchor = null; }, // mouse events onMouseDown: function(e){ // summary: // event processor for onmousedown // e: Event // mouse event if(this.autoSync){ this.sync(); } if(!this.current){ return; } if(!this.singular && !dojo.isCopyKey(e) && !e.shiftKey && (this.current.id in this.selection)){ this.simpleSelection = true; if(e.button === dojo.mouseButtons.LEFT){ // accept the left button and stop the event // for IE we don't stop event when multiple buttons are pressed dojo.stopEvent(e); } return; } if(!this.singular && e.shiftKey){ if(!dojo.isCopyKey(e)){ this._removeSelection(); } var c = this.getAllNodes(); if(c.length){ if(!this.anchor){ this.anchor = c[0]; this._addItemClass(this.anchor, "Anchor"); } this.selection[this.anchor.id] = 1; if(this.anchor != this.current){ var i = 0; for(; i < c.length; ++i){ var node = c[i]; if(node == this.anchor || node == this.current){ break; } } for(++i; i < c.length; ++i){ var node = c[i]; if(node == this.anchor || node == this.current){ break; } this._addItemClass(node, "Selected"); this.selection[node.id] = 1; } this._addItemClass(this.current, "Selected"); this.selection[this.current.id] = 1; } } }else{ if(this.singular){ if(this.anchor == this.current){ if(dojo.isCopyKey(e)){ this.selectNone(); } }else{ this.selectNone(); this.anchor = this.current; this._addItemClass(this.anchor, "Anchor"); this.selection[this.current.id] = 1; } }else{ if(dojo.isCopyKey(e)){ if(this.anchor == this.current){ delete this.selection[this.anchor.id]; this._removeAnchor(); }else{ if(this.current.id in this.selection){ this._removeItemClass(this.current, "Selected"); delete this.selection[this.current.id]; }else{ if(this.anchor){ this._removeItemClass(this.anchor, "Anchor"); this._addItemClass(this.anchor, "Selected"); } this.anchor = this.current; this._addItemClass(this.current, "Anchor"); this.selection[this.current.id] = 1; } } }else{ if(!(this.current.id in this.selection)){ this.selectNone(); this.anchor = this.current; this._addItemClass(this.current, "Anchor"); this.selection[this.current.id] = 1; } } } } dojo.stopEvent(e); }, onMouseUp: function(e){ // summary: // event processor for onmouseup // e: Event // mouse event if(!this.simpleSelection){ return; } this.simpleSelection = false; this.selectNone(); if(this.current){ this.anchor = this.current; this._addItemClass(this.anchor, "Anchor"); this.selection[this.current.id] = 1; } }, onMouseMove: function(e){ // summary: // event processor for onmousemove // e: Event // mouse event this.simpleSelection = false; }, // utilities onOverEvent: function(){ // summary: // this function is called once, when mouse is over our container this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove"); }, onOutEvent: function(){ // summary: // this function is called once, when mouse is out of our container dojo.disconnect(this.onmousemoveEvent); delete this.onmousemoveEvent; }, _removeSelection: function(){ // summary: // unselects all items var e = dojo.dnd._empty; for(var i in this.selection){ if(i in e){ continue; } var node = dojo.byId(i); if(node){ this._removeItemClass(node, "Selected"); } } this.selection = {}; return this; // self }, _removeAnchor: function(){ if(this.anchor){ this._removeItemClass(this.anchor, "Anchor"); this.anchor = null; } return this; // self } }); return dojo.dnd.Selector; }); }, 'dijit/focus':function(){ define("dijit/focus", [ "dojo/aspect", "dojo/_base/declare", // declare "dojo/dom", // domAttr.get dom.isDescendant "dojo/dom-attr", // domAttr.get dom.isDescendant "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy "dojo/Evented", "dojo/_base/lang", // lang.hitch "dojo/on", "dojo/ready", "dojo/_base/sniff", // has("ie") "dojo/Stateful", "dojo/_base/unload", // unload.addOnWindowUnload "dojo/_base/window", // win.body "dojo/window", // winUtils.get "./a11y", // a11y.isTabNavigable "./registry", // registry.byId "." // to set dijit.focus ], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils, a11y, registry, dijit){ // module: // dijit/focus // summary: // Returns a singleton that tracks the currently focused node, and which widgets are currently "active". /*===== dijit.focus = { // summary: // Tracks the currently focused node, and which widgets are currently "active". // Access via require(["dijit/focus"], function(focus){ ... }). // // A widget is considered active if it or a descendant widget has focus, // or if a non-focusable node of this widget or a descendant was recently clicked. // // Call focus.watch("curNode", callback) to track the current focused DOMNode, // or focus.watch("activeStack", callback) to track the currently focused stack of widgets. // // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when // when widgets become active/inactive // // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist. // curNode: DomNode // Currently focused item on screen curNode: null, // activeStack: dijit._Widget[] // List of currently active widgets (focused widget and it's ancestors) activeStack: [], registerIframe: function(iframe){ // summary: // Registers listeners on the specified iframe so that any click // or focus event on that iframe (or anything in it) is reported // as a focus/click event on the