surrounding it) to take all the available space
// except what's needed for the header (toolbars) and footer (breadcrumbs, etc).
// A class was added to the iframe container and some themes style it, so we have to
// calc off the added margins and padding too. See tracker: #10662
var areaHeight = (this._contentBox.h -
(this.getHeaderHeight() + this.getFooterHeight() +
dojo._getPadBorderExtents(this.iframe.parentNode).h +
dojo._getMarginExtents(this.iframe.parentNode).h));
this.editingArea.style.height = areaHeight + "px";
if(this.iframe){
this.iframe.style.height="100%";
}
this._layoutMode = true;
},
_onIEMouseDown: function(/*Event*/ e){
// summary:
// IE only to prevent 2 clicks to focus
// tags:
// private
var outsideClientArea;
// IE 8's componentFromPoint is broken, which is a shame since it
// was smaller code, but oh well. We have to do this brute force
// to detect if the click was scroller or not.
var b = this.document.body;
var clientWidth = b.clientWidth;
var clientHeight = b.clientHeight;
var clientLeft = b.clientLeft;
var offsetWidth = b.offsetWidth;
var offsetHeight = b.offsetHeight;
var offsetLeft = b.offsetLeft;
//Check for vertical scroller click.
bodyDir = b.dir ? b.dir.toLowerCase() : "";
if(bodyDir != "rtl"){
if(clientWidth < offsetWidth && e.x > clientWidth && e.x < offsetWidth){
// Check the click was between width and offset width, if so, scroller
outsideClientArea = true;
}
}else{
// RTL mode, we have to go by the left offsets.
if(e.x < clientLeft && e.x > offsetLeft){
// Check the click was between width and offset width, if so, scroller
outsideClientArea = true;
}
}
if(!outsideClientArea){
// Okay, might be horiz scroller, check that.
if(clientHeight < offsetHeight && e.y > clientHeight && e.y < offsetHeight){
// Horizontal scroller.
outsideClientArea = true;
}
}
if(!outsideClientArea){
delete this._cursorToStart; // Remove the force to cursor to start position.
delete this._savedSelection; // new mouse position overrides old selection
if(e.target.tagName == "BODY"){
setTimeout(dojo.hitch(this, "placeCursorAtEnd"), 0);
}
this.inherited(arguments);
}
},
onBeforeActivate: function(e){
this._restoreSelection();
},
onBeforeDeactivate: function(e){
// summary:
// Called on IE right before focus is lost. Saves the selected range.
// tags:
// private
if(this.customUndo){
this.endEditing(true);
}
//in IE, the selection will be lost when other elements get focus,
//let's save focus before the editor is deactivated
if(e.target.tagName != "BODY"){
this._saveSelection();
}
//console.log('onBeforeDeactivate',this);
},
/* beginning of custom undo/redo support */
// customUndo: Boolean
// Whether we shall use custom undo/redo support instead of the native
// browser support. By default, we now use custom undo. It works better
// than native browser support and provides a consistent behavior across
// browsers with a minimal performance hit. We already had the hit on
// the slowest browser, IE, anyway.
customUndo: true,
// editActionInterval: Integer
// When using customUndo, not every keystroke will be saved as a step.
// Instead typing (including delete) will be grouped together: after
// a user stops typing for editActionInterval seconds, a step will be
// saved; if a user resume typing within editActionInterval seconds,
// the timeout will be restarted. By default, editActionInterval is 3
// seconds.
editActionInterval: 3,
beginEditing: function(cmd){
// summary:
// Called to note that the user has started typing alphanumeric characters, if it's not already noted.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(!this._inEditing){
this._inEditing=true;
this._beginEditing(cmd);
}
if(this.editActionInterval>0){
if(this._editTimer){
clearTimeout(this._editTimer);
}
this._editTimer = setTimeout(dojo.hitch(this, this.endEditing), this._editInterval);
}
},
// TODO: declaring these in the prototype is meaningless, just create in the constructor/postCreate
_steps:[],
_undoedSteps:[],
execCommand: function(cmd){
// summary:
// Main handler for executing any commands to the editor, like paste, bold, etc.
// Called by plugins, but not meant to be called by end users.
// tags:
// protected
if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
return this[cmd]();
}else{
if(this.customUndo){
this.endEditing();
this._beginEditing();
}
var r;
var isClipboard = /copy|cut|paste/.test(cmd);
try{
r = this.inherited(arguments);
if(dojo.isWebKit && isClipboard && !r){ //see #4598: webkit does not guarantee clipboard support from js
throw { code: 1011 }; // throw an object like Mozilla's error
}
}catch(e){
//TODO: when else might we get an exception? Do we need the Mozilla test below?
if(e.code == 1011 /* Mozilla: service denied */ && isClipboard){
// Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
var sub = dojo.string.substitute,
accel = {cut:'X', copy:'C', paste:'V'};
alert(sub(this.commands.systemShortcut,
[this.commands[cmd], sub(this.commands[dojo.isMac ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
}
r = false;
}
if(this.customUndo){
this._endEditing();
}
return r;
}
},
queryCommandEnabled: function(cmd){
// summary:
// Returns true if specified editor command is enabled.
// Used by the plugins to know when to highlight/not highlight buttons.
// tags:
// protected
if(this.customUndo && (cmd == 'undo' || cmd == 'redo')){
return cmd == 'undo' ? (this._steps.length > 1) : (this._undoedSteps.length > 0);
}else{
return this.inherited(arguments);
}
},
_moveToBookmark: function(b){
// summary:
// Selects the text specified in bookmark b
// tags:
// private
var bookmark = b.mark;
var mark = b.mark;
var col = b.isCollapsed;
var r, sNode, eNode, sel;
if(mark){
if(dojo.isIE < 9){
if(dojo.isArray(mark)){
//IE CONTROL, have to use the native bookmark.
bookmark = [];
dojo.forEach(mark,function(n){
bookmark.push(dijit.range.getNode(n,this.editNode));
},this);
dojo.withGlobal(this.window,'moveToBookmark',dijit,[{mark: bookmark, isCollapsed: col}]);
}else{
if(mark.startContainer && mark.endContainer){
// Use the pseudo WC3 range API. This works better for positions
// than the IE native bookmark code.
sel = dijit.range.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = dijit.range.create(this.window);
sNode = dijit.range.getNode(mark.startContainer,this.editNode);
eNode = dijit.range.getNode(mark.endContainer,this.editNode);
if(sNode && eNode){
// Okay, we believe we found the position, so add it into the selection
// There are cases where it may not be found, particularly in undo/redo, when
// IE changes the underlying DOM on us (wraps text in a
tag or similar.
// So, in those cases, don't bother restoring selection.
r.setStart(sNode,mark.startOffset);
r.setEnd(eNode,mark.endOffset);
sel.addRange(r);
}
}
}
}
}else{//w3c range
sel = dijit.range.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = dijit.range.create(this.window);
sNode = dijit.range.getNode(mark.startContainer,this.editNode);
eNode = dijit.range.getNode(mark.endContainer,this.editNode);
if(sNode && eNode){
// Okay, we believe we found the position, so add it into the selection
// There are cases where it may not be found, particularly in undo/redo, when
// formatting as been done and so on, so don't restore selection then.
r.setStart(sNode,mark.startOffset);
r.setEnd(eNode,mark.endOffset);
sel.addRange(r);
}
}
}
}
},
_changeToStep: function(from, to){
// summary:
// Reverts editor to "to" setting, from the undo stack.
// tags:
// private
this.setValue(to.text);
var b=to.bookmark;
if(!b){ return; }
this._moveToBookmark(b);
},
undo: function(){
// summary:
// Handler for editor undo (ex: ctrl-z) operation
// tags:
// private
//console.log('undo');
var ret = false;
if(!this._undoRedoActive){
this._undoRedoActive = true;
this.endEditing(true);
var s=this._steps.pop();
if(s && this._steps.length>0){
this.focus();
this._changeToStep(s,this._steps[this._steps.length-1]);
this._undoedSteps.push(s);
this.onDisplayChanged();
delete this._undoRedoActive;
ret = true;
}
delete this._undoRedoActive;
}
return ret;
},
redo: function(){
// summary:
// Handler for editor redo (ex: ctrl-y) operation
// tags:
// private
//console.log('redo');
var ret = false;
if(!this._undoRedoActive){
this._undoRedoActive = true;
this.endEditing(true);
var s=this._undoedSteps.pop();
if(s && this._steps.length>0){
this.focus();
this._changeToStep(this._steps[this._steps.length-1],s);
this._steps.push(s);
this.onDisplayChanged();
ret = true;
}
delete this._undoRedoActive;
}
return ret;
},
endEditing: function(ignore_caret){
// summary:
// Called to note that the user has stopped typing alphanumeric characters, if it's not already noted.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(this._editTimer){
clearTimeout(this._editTimer);
}
if(this._inEditing){
this._endEditing(ignore_caret);
this._inEditing=false;
}
},
_getBookmark: function(){
// summary:
// Get the currently selected text
// tags:
// protected
var b=dojo.withGlobal(this.window,dijit.getBookmark);
var tmp=[];
if(b && b.mark){
var mark = b.mark;
if(dojo.isIE < 9){
// Try to use the pseudo range API on IE for better accuracy.
var sel = dijit.range.getSelection(this.window);
if(!dojo.isArray(mark)){
if(sel){
var range;
if(sel.rangeCount){
range = sel.getRangeAt(0);
}
if(range){
b.mark = range.cloneRange();
}else{
b.mark = dojo.withGlobal(this.window,dijit.getBookmark);
}
}
}else{
// Control ranges (img, table, etc), handle differently.
dojo.forEach(b.mark,function(n){
tmp.push(dijit.range.getIndex(n,this.editNode).o);
},this);
b.mark = tmp;
}
}
try{
if(b.mark && b.mark.startContainer){
tmp=dijit.range.getIndex(b.mark.startContainer,this.editNode).o;
b.mark={startContainer:tmp,
startOffset:b.mark.startOffset,
endContainer:b.mark.endContainer===b.mark.startContainer?tmp:dijit.range.getIndex(b.mark.endContainer,this.editNode).o,
endOffset:b.mark.endOffset};
}
}catch(e){
b.mark = null;
}
}
return b;
},
_beginEditing: function(cmd){
// summary:
// Called when the user starts typing alphanumeric characters.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
if(this._steps.length === 0){
// You want to use the editor content without post filtering
// to make sure selection restores right for the 'initial' state.
// and undo is called. So not using this.value, as it was 'processed'
// and the line-up for selections may have been altered.
this._steps.push({'text':dijit._editor.getChildrenHtml(this.editNode),'bookmark':this._getBookmark()});
}
},
_endEditing: function(ignore_caret){
// summary:
// Called when the user stops typing alphanumeric characters.
// Deals with saving undo; see editActionInterval parameter.
// tags:
// private
// Avoid filtering to make sure selections restore.
var v = dijit._editor.getChildrenHtml(this.editNode);
this._undoedSteps=[];//clear undoed steps
this._steps.push({text: v, bookmark: this._getBookmark()});
},
onKeyDown: function(e){
// summary:
// Handler for onkeydown event.
// tags:
// private
//We need to save selection if the user TAB away from this editor
//no need to call _saveSelection for IE, as that will be taken care of in onBeforeDeactivate
if(!dojo.isIE && !this.iframe && e.keyCode == dojo.keys.TAB && !this.tabIndent){
this._saveSelection();
}
if(!this.customUndo){
this.inherited(arguments);
return;
}
var k = e.keyCode, ks = dojo.keys;
if(e.ctrlKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
if(k == 90 || k == 122){ //z
dojo.stopEvent(e);
this.undo();
return;
}else if(k == 89 || k == 121){ //y
dojo.stopEvent(e);
this.redo();
return;
}
}
this.inherited(arguments);
switch(k){
case ks.ENTER:
case ks.BACKSPACE:
case ks.DELETE:
this.beginEditing();
break;
case 88: //x
case 86: //v
if(e.ctrlKey && !e.altKey && !e.metaKey){
this.endEditing();//end current typing step if any
if(e.keyCode == 88){
this.beginEditing('cut');
//use timeout to trigger after the cut is complete
setTimeout(dojo.hitch(this, this.endEditing), 1);
}else{
this.beginEditing('paste');
//use timeout to trigger after the paste is complete
setTimeout(dojo.hitch(this, this.endEditing), 1);
}
break;
}
//pass through
default:
if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCodedojo.keys.F15)){
this.beginEditing();
break;
}
//pass through
case ks.ALT:
this.endEditing();
break;
case ks.UP_ARROW:
case ks.DOWN_ARROW:
case ks.LEFT_ARROW:
case ks.RIGHT_ARROW:
case ks.HOME:
case ks.END:
case ks.PAGE_UP:
case ks.PAGE_DOWN:
this.endEditing(true);
break;
//maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
case ks.CTRL:
case ks.SHIFT:
case ks.TAB:
break;
}
},
_onBlur: function(){
// summary:
// Called from focus manager when focus has moved away from this editor
// tags:
// protected
//this._saveSelection();
this.inherited(arguments);
this.endEditing(true);
},
_saveSelection: function(){
// summary:
// Save the currently selected text in _savedSelection attribute
// tags:
// private
try{
this._savedSelection=this._getBookmark();
}catch(e){ /* Squelch any errors that occur if selection save occurs due to being hidden simultaniously. */}
},
_restoreSelection: function(){
// summary:
// Re-select the text specified in _savedSelection attribute;
// see _saveSelection().
// tags:
// private
if(this._savedSelection){
// Clear off cursor to start, we're deliberately going to a selection.
delete this._cursorToStart;
// only restore the selection if the current range is collapsed
// if not collapsed, then it means the editor does not lose
// selection and there is no need to restore it
if(dojo.withGlobal(this.window,'isCollapsed',dijit)){
this._moveToBookmark(this._savedSelection);
}
delete this._savedSelection;
}
},
onClick: function(){
// summary:
// Handler for when editor is clicked
// tags:
// protected
this.endEditing(true);
this.inherited(arguments);
},
replaceValue: function(/*String*/ html){
// summary:
// over-ride of replaceValue to support custom undo and stack maintainence.
// tags:
// protected
if(!this.customUndo){
this.inherited(arguments);
}else{
if(this.isClosed){
this.setValue(html);
}else{
this.beginEditing();
if(!html){
html = " "
}
this.setValue(html);
this.endEditing();
}
}
},
_setDisabledAttr: function(/*Boolean*/ value){
var disableFunc = dojo.hitch(this, function(){
if((!this.disabled && value) || (!this._buttonEnabledPlugins && value)){
// Disable editor: disable all enabled buttons and remember that list
dojo.forEach(this._plugins, function(p){
p.set("disabled", true);
});
}else if(this.disabled && !value){
// Restore plugins to being active.
dojo.forEach(this._plugins, function(p){
p.set("disabled", false);
});
}
});
this.setValueDeferred.addCallback(disableFunc);
this.inherited(arguments);
},
_setStateClass: function(){
try{
this.inherited(arguments);
// Let theme set the editor's text color based on editor enabled/disabled state.
// We need to jump through hoops because the main document (where the theme CSS is)
// is separate from the iframe's document.
if(this.document && this.document.body){
dojo.style(this.document.body, "color", dojo.style(this.iframe, "color"));
}
}catch(e){ /* Squelch any errors caused by focus change if hidden during a state change */}
}
}
);
// Register the "default plugins", ie, the built-in editor commands
dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
if(o.plugin){ return; }
var args = o.args, p;
var _p = dijit._editor._Plugin;
var name = args.name;
switch(name){
case "undo": case "redo": case "cut": case "copy": case "paste": case "insertOrderedList":
case "insertUnorderedList": case "indent": case "outdent": case "justifyCenter":
case "justifyFull": case "justifyLeft": case "justifyRight": case "delete":
case "selectAll": case "removeFormat": case "unlink":
case "insertHorizontalRule":
p = new _p({ command: name });
break;
case "bold": case "italic": case "underline": case "strikethrough":
case "subscript": case "superscript":
p = new _p({ buttonClass: dijit.form.ToggleButton, command: name });
break;
case "|":
p = new _p({ button: new dijit.ToolbarSeparator(), setEditor: function(editor) {this.editor = editor;} });
}
// console.log('name',name,p);
o.plugin=p;
});
}
if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.ToggleButton"] = true;
dojo.provide("dijit.form.ToggleButton");
}
if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.CheckBox"] = true;
dojo.provide("dijit.form.CheckBox");
dojo.declare(
"dijit.form.CheckBox",
dijit.form.ToggleButton,
{
// summary:
// Same as an HTML checkbox, but with fancy styling.
//
// description:
// User interacts with real html inputs.
// On onclick (which occurs by mouse click, space-bar, or
// using the arrow keys to switch the selected radio button),
// we update the state of the checkbox/radio.
//
// There are two modes:
// 1. High contrast mode
// 2. Normal mode
//
// In case 1, the regular html inputs are shown and used by the user.
// In case 2, the regular html inputs are invisible but still used by
// the user. They are turned quasi-invisible and overlay the background-image.
templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "
\n"),
baseClass: "dijitCheckBox",
// type: [private] String
// type attribute on node.
// Overrides `dijit.form.Button.type`. Users should not change this value.
type: "checkbox",
// value: String
// As an initialization parameter, equivalent to value field on normal checkbox
// (if checked, the value is passed as the value when form is submitted).
//
// However, get('value') will return either the string or false depending on
// whether or not the checkbox is checked.
//
// set('value', string) will check the checkbox and change the value to the
// specified string
//
// set('value', boolean) will change the checked state.
value: "on",
// readOnly: Boolean
// Should this widget respond to user input?
// In markup, this is specified as "readOnly".
// Similar to disabled except readOnly form values are submitted.
readOnly: false,
// the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
// instead of ToggleButton as the icon mapping has no meaning for a CheckBox
attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
readOnly: "focusNode"
}),
_setReadOnlyAttr: function(/*Boolean*/ value){
this._set("readOnly", value);
dojo.attr(this.focusNode, 'readOnly', value);
dijit.setWaiState(this.focusNode, "readonly", value);
},
_setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
// summary:
// Handler for value= attribute to constructor, and also calls to
// set('value', val).
// description:
// During initialization, just saves as attribute to the .
//
// After initialization,
// when passed a boolean, controls whether or not the CheckBox is checked.
// If passed a string, changes the value attribute of the CheckBox (the one
// specified as "value" when the CheckBox was constructed (ex: )
if(typeof newValue == "string"){
this._set("value", newValue);
dojo.attr(this.focusNode, 'value', newValue);
newValue = true;
}
if(this._created){
this.set('checked', newValue, priorityChange);
}
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// description:
// If the CheckBox is checked, returns the value attribute.
// Otherwise returns false.
return (this.checked ? this.value : false);
},
// Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
// Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
_setLabelAttr: undefined,
postMixInProperties: function(){
if(this.value == ""){
this.value = "on";
}
// Need to set initial checked state as part of template, so that form submit works.
// dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
// to , see #8666
this.checkedAttrSetting = this.checked ? "checked" : "";
this.inherited(arguments);
},
_fillContent: function(/*DomNode*/ source){
// Override Button::_fillContent() since it doesn't make sense for CheckBox,
// since CheckBox doesn't even have a container
},
reset: function(){
// Override ToggleButton.reset()
this._hasBeenBlurred = false;
this.set('checked', this.params.checked || false);
// Handle unlikely event that the value attribute has changed
this._set("value", this.params.value || "on");
dojo.attr(this.focusNode, 'value', this.value);
},
_onFocus: function(){
if(this.id){
dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
}
this.inherited(arguments);
},
_onBlur: function(){
if(this.id){
dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
}
this.inherited(arguments);
},
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions - need to check
// readOnly, since button no longer does that check.
if(this.readOnly){
dojo.stopEvent(e);
return false;
}
return this.inherited(arguments);
}
}
);
dojo.declare(
"dijit.form.RadioButton",
dijit.form.CheckBox,
{
// summary:
// Same as an HTML radio, but with fancy styling.
type: "radio",
baseClass: "dijitRadio",
_setCheckedAttr: function(/*Boolean*/ value){
// If I am being checked then have to deselect currently checked radio button
this.inherited(arguments);
if(!this._created){ return; }
if(value){
var _this = this;
// search for radio buttons with the same name that need to be unchecked
dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
function(inputNode){
if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
var widget = dijit.getEnclosingWidget(inputNode);
if(widget && widget.checked){
widget.set('checked', false);
}
}
}
);
}
},
_clicked: function(/*Event*/ e){
if(!this.checked){
this.set('checked', true);
}
}
}
);
}
if(!dojo._hasResource["dijit.Calendar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.Calendar"] = true;
dojo.provide("dijit.Calendar");
dojo.declare(
"dijit.Calendar",
[dijit._Widget, dijit._Templated, dijit._CssStateMixin],
{
// summary:
// A simple GUI for choosing a date in the context of a monthly calendar.
//
// description:
// A simple GUI for choosing a date in the context of a monthly calendar.
// This widget can't be used in a form because it doesn't serialize the date to an
// ` ` field. For a form element, use dijit.form.DateTextBox instead.
//
// Note that the parser takes all dates attributes passed in the
// [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
// so that they are serializable and locale-independent.
//
// example:
// | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode"));
//
// example:
// |
templateString: dojo.cache("dijit", "templates/Calendar.html", "\n\t\n\t\t\n\t\t\t\n\t\t\t\t \n\t\t\t\t- \n\t\t\t \n\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t \n\t\t\t\n\t\t\t\t \n\t\t\t\t+ \n\t\t\t \n\t\t \n\t\t\n\t\t\t \n\t\t \n\t \n\t\n\t\t\n\t\t\t \n\t\t \n\t \n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t\t \n\t\t\t\t \n\t\t\t \n\t\t \n\t \n
\n"),
widgetsInTemplate: true,
// value: Date
// The currently selected Date, initially set to invalid date to indicate no selection.
value: new Date(""),
// TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String
// JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines
// at dojo.date by default.
datePackage: "dojo.date",
// dayWidth: String
// How to represent the days of the week in the calendar header. See dojo.date.locale
dayWidth: "narrow",
// tabIndex: Integer
// Order fields are traversed when user hits the tab key
tabIndex: "0",
// currentFocus: Date
// Date object containing the currently focused date, or the date which would be focused
// if the calendar itself was focused. Also indicates which year and month to display,
// i.e. the current "page" the calendar is on.
currentFocus: new Date(),
baseClass:"dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details
cssStateNodes: {
"decrementMonth": "dijitCalendarArrow",
"incrementMonth": "dijitCalendarArrow",
"previousYearLabelNode": "dijitCalendarPreviousYear",
"nextYearLabelNode": "dijitCalendarNextYear"
},
_isValidDate: function(/*Date*/ value){
// summary:
// Runs various tests on the value, checking that it's a valid date, rather
// than blank or NaN.
// tags:
// private
return value && !isNaN(value) && typeof value == "object" &&
value.toString() != this.constructor.prototype.value.toString();
},
setValue: function(/*Date*/ value){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
this.set('value', value);
},
_getValueAttr: function(){
// summary:
// Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat
var value = new this.dateClassObj(this.value);
value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date
// object to point at 1am so it will represent the correct day. See #9366
if(value.getDate() < this.value.getDate()){
value = this.dateFuncObj.add(value, "hour", 1);
}
return value;
},
_setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){
// summary:
// Support set("value", ...)
// description:
// Set the current date and update the UI. If the date is disabled, the value will
// not change, but the display will change to the corresponding month.
// value:
// Either a Date or the number of seconds since 1970.
// tags:
// protected
if(value){
// convert from Number to Date, or make copy of Date object so that setHours() call below
// doesn't affect original value
value = new this.dateClassObj(value);
}
if(this._isValidDate(value)){
if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){
value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
if(!this.isDisabledDate(value, this.lang)){
this._set("value", value);
// Set focus cell to the new value. Arguably this should only happen when there isn't a current
// focus point. This will also repopulate the grid, showing the new selected value (and possibly
// new month/year).
this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){
this.onChange(this.get('value'));
this.onValueSelected(this.get('value')); // remove in 2.0
}
}
}
}else{
// clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus
this._set("value", null);
this.set("currentFocus", this.currentFocus);
}
},
_setText: function(node, text){
// summary:
// This just sets the content of node to the specified text.
// Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434.
// tags:
// private
while(node.firstChild){
node.removeChild(node.firstChild);
}
node.appendChild(dojo.doc.createTextNode(text));
},
_populateGrid: function(){
// summary:
// Fills in the calendar grid with each day (1-31)
// tags:
// private
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
var firstDay = month.getDay(),
daysInMonth = this.dateFuncObj.getDaysInMonth(month),
daysInPreviousMonth = this.dateFuncObj.getDaysInMonth(this.dateFuncObj.add(month, "month", -1)),
today = new this.dateClassObj(),
dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang);
if(dayOffset > firstDay){ dayOffset -= 7; }
// Iterate through dates in the calendar and fill in date numbers and style info
dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){
i += dayOffset;
var date = new this.dateClassObj(month),
number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){
number = daysInPreviousMonth - firstDay + i + 1;
adj = -1;
clazz += "Previous";
}else if(i >= (firstDay + daysInMonth)){
number = i - firstDay - daysInMonth + 1;
adj = 1;
clazz += "Next";
}else{
number = i - firstDay + 1;
clazz += "Current";
}
if(adj){
date = this.dateFuncObj.add(date, "month", adj);
}
date.setDate(number);
if(!this.dateFuncObj.compare(date, today, "date")){
clazz = "dijitCalendarCurrentDate " + clazz;
}
if(this._isSelectedDate(date, this.lang)){
clazz = "dijitCalendarSelectedDate " + clazz;
}
if(this.isDisabledDate(date, this.lang)){
clazz = "dijitCalendarDisabledDate " + clazz;
}
var clazz2 = this.getClassForDate(date, this.lang);
if(clazz2){
clazz = clazz2 + " " + clazz;
}
template.className = clazz + "Month dijitCalendarDateTemplate";
template.dijitDateValue = date.valueOf(); // original code
dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it
var label = dojo.query(".dijitCalendarDateLabel", template)[0],
text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate();
this._setText(label, text);
}, this);
// Repopulate month drop down list based on current year.
// Need to do this to hide leap months in Hebrew calendar.
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
this.monthDropDownButton.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names
// (invisible) so that the maximum width will affect layout. But not on IE6 because then
// the center overlaps the right (due to a browser bug).
this.monthDropDownButton.containerNode.innerHTML =
(dojo.isIE == 6 ? "" : "" + this.monthDropDownButton.dropDown.domNode.innerHTML + "
") +
"" + monthNames[month.getMonth()] + "
";
// Fill in localized prev/current/next years
var y = month.getFullYear() - 1;
var d = new this.dateClassObj();
dojo.forEach(["previous", "current", "next"], function(name){
d.setFullYear(y++);
this._setText(this[name+"YearLabelNode"],
this.dateLocaleModule.format(d, {selector:'year', locale:this.lang}));
}, this);
},
goToToday: function(){
// summary:
// Sets calendar's value to today's date
this.set('value', new this.dateClassObj());
},
constructor: function(/*Object*/args){
var dateClass = (args.datePackage && (args.datePackage != "dojo.date"))? args.datePackage + ".Date" : "Date";
this.dateClassObj = dojo.getObject(dateClass, false);
this.datePackage = args.datePackage || this.datePackage;
this.dateFuncObj = dojo.getObject(this.datePackage, false);
this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
},
postMixInProperties: function(){
// Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead.
// TODO: remove this for 2.0 (thanks to #11511)
if(isNaN(this.value)){ delete this.value; }
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
dojo.setSelectable(this.domNode, false);
var cloneClass = dojo.hitch(this, function(clazz, n){
var template = dojo.query(clazz, this.domNode)[0];
for(var i=0; i= 0){ _this._adjustDisplay(dateProp, adj); }
}, 0.8, 500)
);
};
typematic("incrementMonth", "month", 1);
typematic("decrementMonth", "month", -1);
typematic("nextYearLabelNode", "year", 1);
typematic("previousYearLabelNode", "year", -1);
},
_adjustDisplay: function(/*String*/ part, /*int*/ amount){
// summary:
// Moves calendar forwards or backwards by months or years
// part:
// "month" or "year"
// amount:
// Number of months or years
// tags:
// private
this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount));
},
_setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){
// summary:
// If the calendar currently has focus, then focuses specified date,
// changing the currently displayed month/year if necessary.
// If the calendar doesn't have focus, updates currently
// displayed month/year, and sets the cell that will get focus.
// forceFocus:
// If true, will focus() the cell even if calendar itself doesn't have focus
var oldFocus = this.currentFocus,
oldCell = oldFocus ? dojo.query("[dijitDateValue=" + oldFocus.valueOf() + "]", this.domNode)[0] : null;
// round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
date = new this.dateClassObj(date);
date.setHours(1, 0, 0, 0);
this._set("currentFocus", date);
// TODO: only re-populate grid when month/year has changed
this._populateGrid();
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0];
newCell.setAttribute("tabIndex", this.tabIndex);
if(this._focused || forceFocus){
newCell.focus();
}
// set tabIndex=-1 on old focusable cell
if(oldCell && oldCell != newCell){
if(dojo.isWebKit){ // see #11064 about webkit bug
oldCell.setAttribute("tabIndex", "-1");
}else{
oldCell.removeAttribute("tabIndex");
}
}
},
focus: function(){
// summary:
// Focus the calendar by focusing one of the calendar cells
this._setCurrentFocusAttr(this.currentFocus, true);
},
_onMonthSelect: function(/*Number*/ newMonth){
// summary:
// Handler for when user selects a month from the drop down list
// tags:
// protected
// move to selected month, bounding by the number of days in the month
// (ex: dec 31 --> jan 28, not jan 31)
this.currentFocus = this.dateFuncObj.add(this.currentFocus, "month",
newMonth - this.currentFocus.getMonth());
this._populateGrid();
},
_onDayClick: function(/*Event*/ evt){
// summary:
// Handler for day clicks, selects the date if appropriate
// tags:
// protected
dojo.stopEvent(evt);
for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode);
if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){
this.set('value', node.dijitDateValue);
}
},
_onDayMouseOver: function(/*Event*/ evt){
// summary:
// Handler for mouse over events on days, sets hovered style
// tags:
// protected
// event can occur on or the inside the td,
// set node to the .
var node =
dojo.hasClass(evt.target, "dijitCalendarDateLabel") ?
evt.target.parentNode :
evt.target;
if(node && (node.dijitDateValue || node == this.previousYearLabelNode || node == this.nextYearLabelNode) ){
dojo.addClass(node, "dijitCalendarHoveredDate");
this._currentNode = node;
}
},
_onDayMouseOut: function(/*Event*/ evt){
// summary:
// Handler for mouse out events on days, clears hovered style
// tags:
// protected
if(!this._currentNode){ return; }
// if mouse out occurs moving from to inside , ignore it
if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){ return; }
var cls = "dijitCalendarHoveredDate";
if(dojo.hasClass(this._currentNode, "dijitCalendarActiveDate")) {
cls += " dijitCalendarActiveDate";
}
dojo.removeClass(this._currentNode, cls);
this._currentNode = null;
},
_onDayMouseDown: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue){
dojo.addClass(node, "dijitCalendarActiveDate");
this._currentNode = node;
}
},
_onDayMouseUp: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue){
dojo.removeClass(node, "dijitCalendarActiveDate");
}
},
//TODO: use typematic
handleKey: function(/*Event*/ evt){
// summary:
// Provides keyboard navigation of calendar.
// description:
// Called from _onKeyPress() to handle keypress on a stand alone Calendar,
// and also from `dijit.form._DateTimeTextBox` to pass a keypress event
// from the `dijit.form.DateTextBox` to be handled in this widget
// returns:
// False if the key was recognized as a navigation key,
// to indicate that the event was handled by Calendar and shouldn't be propogated
// tags:
// protected
var dk = dojo.keys,
increment = -1,
interval,
newValue = this.currentFocus;
switch(evt.keyCode){
case dk.RIGHT_ARROW:
increment = 1;
//fallthrough...
case dk.LEFT_ARROW:
interval = "day";
if(!this.isLeftToRight()){ increment *= -1; }
break;
case dk.DOWN_ARROW:
increment = 1;
//fallthrough...
case dk.UP_ARROW:
interval = "week";
break;
case dk.PAGE_DOWN:
increment = 1;
//fallthrough...
case dk.PAGE_UP:
interval = evt.ctrlKey || evt.altKey ? "year" : "month";
break;
case dk.END:
// go to the next month
newValue = this.dateFuncObj.add(newValue, "month", 1);
// subtract a day from the result when we're done
interval = "day";
//fallthrough...
case dk.HOME:
newValue = new this.dateClassObj(newValue);
newValue.setDate(1);
break;
case dk.ENTER:
case dk.SPACE:
this.set("value", this.currentFocus);
break;
default:
return true;
}
if(interval){
newValue = this.dateFuncObj.add(newValue, interval, increment);
}
this._setCurrentFocusAttr(newValue);
return false;
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// For handling keypress events on a stand alone calendar
if(!this.handleKey(evt)){
dojo.stopEvent(evt);
}
},
onValueSelected: function(/*Date*/ date){
// summary:
// Notification that a date cell was selected. It may be the same as the previous value.
// description:
// Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
// to get notification when the user has clicked a date. Now onExecute() (above) is used.
// tags:
// protected
},
onChange: function(/*Date*/ date){
// summary:
// Called only when the selected date has changed
},
_isSelectedDate: function(/*Date*/ dateObject, /*String?*/ locale){
// summary:
// Extension point so developers can subclass Calendar to
// support multiple (concurrently) selected dates
// tags:
// protected extension
return this._isValidDate(this.value) && !this.dateFuncObj.compare(dateObject, this.value, "date")
},
isDisabledDate: function(/*Date*/ dateObject, /*String?*/ locale){
// summary:
// May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
// tags:
// extension
/*=====
return false; // Boolean
=====*/
},
getClassForDate: function(/*Date*/ dateObject, /*String?*/ locale){
// summary:
// May be overridden to return CSS classes to associate with the date entry for the given dateObject,
// for example to indicate a holiday in specified locale.
// tags:
// extension
/*=====
return ""; // String
=====*/
}
}
);
dojo.declare("dijit.Calendar._MonthDropDown", [dijit._Widget, dijit._Templated], {
// summary:
// The month drop down
// months: String[]
// List of names of months, possibly w/some undefined entries for Hebrew leap months
// (ex: ["January", "February", undefined, "April", ...])
months: [],
templateString: "",
_setMonthsAttr: function(/*String[]*/ months){
this.domNode.innerHTML = dojo.map(months, function(month, idx){
return month ? "" + month + "
" : "";
}).join("");
},
_onClick: function(/*Event*/ evt){
this.onChange(dojo.attr(evt.target, "month"));
},
onChange: function(/*Number*/ month){
// summary:
// Callback when month is selected from drop down
},
_onMenuHover: function(evt){
dojo.toggleClass(evt.target, "dijitCalendarMonthLabelHover", evt.type == "mouseover");
}
});
}
if(!dojo._hasResource["dijit.form._DateTimeTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form._DateTimeTextBox"] = true;
dojo.provide("dijit.form._DateTimeTextBox");
new Date("X"); // workaround for #11279, new Date("") == NaN
/*=====
dojo.declare(
"dijit.form._DateTimeTextBox.__Constraints",
[dijit.form.RangeBoundTextBox.__Constraints, dojo.date.locale.__FormatOptions], {
// summary:
// Specifies both the rules on valid/invalid values (first/last date/time allowed),
// and also formatting options for how the date/time is displayed.
// example:
// To restrict to dates within 2004, displayed in a long format like "December 25, 2005":
// | {min:'2004-01-01',max:'2004-12-31', formatLength:'long'}
});
=====*/
dojo.declare(
"dijit.form._DateTimeTextBox",
[ dijit.form.RangeBoundTextBox, dijit._HasDropDown ],
{
// summary:
// Base class for validating, serializable, range-bound date or time text box.
templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "\n"),
// hasDownArrow: [const] Boolean
// Set this textbox to display a down arrow button, to open the drop down list.
hasDownArrow: true,
// openOnClick: [const] Boolean
// Set to true to open drop down upon clicking anywhere on the textbox.
openOnClick: true,
/*=====
// constraints: dijit.form._DateTimeTextBox.__Constraints
// Despite the name, this parameter specifies both constraints on the input
// (including starting/ending dates/times allowed) as well as
// formatting options like whether the date is displayed in long (ex: December 25, 2005)
// or short (ex: 12/25/2005) format. See `dijit.form._DateTimeTextBox.__Constraints` for details.
constraints: {},
======*/
// Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
// than a straight regexp to deal with locale (plus formatting options too?)
regExpGen: dojo.date.locale.regexp,
// datePackage: String
// JavaScript namespace to find calendar routines. Uses Gregorian calendar routines
// at dojo.date, by default.
datePackage: "dojo.date",
// Override _FormWidget.compare() to work for dates/times
compare: function(/*Date*/ val1, /*Date*/ val2){
var isInvalid1 = this._isInvalidDate(val1);
var isInvalid2 = this._isInvalidDate(val2);
return isInvalid1 ? (isInvalid2 ? 0 : -1) : (isInvalid2 ? 1 : dojo.date.compare(val1, val2, this._selector));
},
// flag to _HasDropDown to make drop down Calendar width == width
forceWidth: true,
format: function(/*Date*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
// summary:
// Formats the value as a Date, according to specified locale (second argument)
// tags:
// protected
if(!value){ return ''; }
return this.dateLocaleModule.format(value, constraints);
},
"parse": function(/*String*/ value, /*dojo.date.locale.__FormatOptions*/ constraints){
// summary:
// Parses as string as a Date, according to constraints
// tags:
// protected
return this.dateLocaleModule.parse(value, constraints) || (this._isEmpty(value) ? null : undefined); // Date
},
// Overrides ValidationTextBox.serialize() to serialize a date in canonical ISO format.
serialize: function(/*anything*/ val, /*Object?*/ options){
if(val.toGregorian){
val = val.toGregorian();
}
return dojo.date.stamp.toISOString(val, options);
},
// dropDownDefaultValue: Date
// The default value to focus in the popupClass widget when the textbox value is empty.
dropDownDefaultValue : new Date(),
// value: Date
// The value of this widget as a JavaScript Date object. Use get("value") / set("value", val) to manipulate.
// When passed to the parser in markup, must be specified according to `dojo.date.stamp.fromISOString`
value: new Date(""), // value.toString()="NaN"
_blankValue: null, // used by filter() when the textbox is blank
// popupClass: [protected extension] String
// Name of the popup widget class used to select a date/time.
// Subclasses should specify this.
popupClass: "", // default is no popup = text only
// _selector: [protected extension] String
// Specifies constraints.selector passed to dojo.date functions, should be either
// "date" or "time".
// Subclass must specify this.
_selector: "",
constructor: function(/*Object*/ args){
var dateClass = args.datePackage ? args.datePackage + ".Date" : "Date";
this.dateClassObj = dojo.getObject(dateClass, false);
this.value = new this.dateClassObj("");
this.datePackage = args.datePackage || this.datePackage;
this.dateLocaleModule = dojo.getObject(this.datePackage + ".locale", false);
this.regExpGen = this.dateLocaleModule.regexp;
this._invalidDate = dijit.form._DateTimeTextBox.prototype.value.toString();
},
buildRendering: function(){
this.inherited(arguments);
if(!this.hasDownArrow){
this._buttonNode.style.display = "none";
}
// If openOnClick is true, we basically just want to treat the whole widget as the
// button. We need to do that also if the actual drop down button will be hidden,
// so that there's a mouse method for opening the drop down.
if(this.openOnClick || !this.hasDownArrow){
this._buttonNode = this.domNode;
this.baseClass += " dijitComboBoxOpenOnClick";
}
},
_setConstraintsAttr: function(/*Object*/ constraints){
constraints.selector = this._selector;
constraints.fullYear = true; // see #5465 - always format with 4-digit years
var fromISO = dojo.date.stamp.fromISOString;
if(typeof constraints.min == "string"){ constraints.min = fromISO(constraints.min); }
if(typeof constraints.max == "string"){ constraints.max = fromISO(constraints.max); }
this.inherited(arguments);
},
_isInvalidDate: function(/*Date*/ value){
// summary:
// Runs various tests on the value, checking for invalid conditions
// tags:
// private
return !value || isNaN(value) || typeof value != "object" || value.toString() == this._invalidDate;
},
_setValueAttr: function(/*Date|String*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Sets the date on this textbox. Note: value can be a JavaScript Date literal or a string to be parsed.
if(value !== undefined){
if(typeof value == "string"){
value = dojo.date.stamp.fromISOString(value);
}
if(this._isInvalidDate(value)){
value = null;
}
if(value instanceof Date && !(this.dateClassObj instanceof Date)){
value = new this.dateClassObj(value);
}
}
this.inherited(arguments);
if(this.value instanceof Date){
this.filterString = "";
}
if(this.dropDown){
this.dropDown.set('value', value, false);
}
},
_set: function(attr, value){
// Avoid spurious watch() notifications when value is changed to new Date object w/the same value
if(attr == "value" && this.value instanceof Date && this.compare(value, this.value) == 0){
return;
}
this.inherited(arguments);
},
_setDropDownDefaultValueAttr: function(/*Date*/ val){
if(this._isInvalidDate(val)){
// convert null setting into today's date, since there needs to be *some* default at all times.
val = new this.dateClassObj();
}
this.dropDownDefaultValue = val;
},
openDropDown: function(/*Function*/ callback){
// rebuild drop down every time, so that constraints get copied (#6002)
if(this.dropDown){
this.dropDown.destroy();
}
var PopupProto = dojo.getObject(this.popupClass, false),
textBox = this,
value = this.get("value");
this.dropDown = new PopupProto({
onChange: function(value){
// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
textBox.set('value', value, true);
},
id: this.id + "_popup",
dir: textBox.dir,
lang: textBox.lang,
value: value,
currentFocus: !this._isInvalidDate(value) ? value : this.dropDownDefaultValue,
constraints: textBox.constraints,
filterString: textBox.filterString, // for TimeTextBox, to filter times shown
datePackage: textBox.datePackage,
isDisabledDate: function(/*Date*/ date){
// summary:
// disables dates outside of the min/max of the _DateTimeTextBox
return !textBox.rangeCheck(date, textBox.constraints);
}
});
this.inherited(arguments);
},
_getDisplayedValueAttr: function(){
return this.textbox.value;
},
_setDisplayedValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
this._setValueAttr(this.parse(value, this.constraints), priorityChange, value);
}
}
);
}
if(!dojo._hasResource["dijit.form.DateTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.DateTextBox"] = true;
dojo.provide("dijit.form.DateTextBox");
dojo.declare(
"dijit.form.DateTextBox",
dijit.form._DateTimeTextBox,
{
// summary:
// A validating, serializable, range-bound date text box with a drop down calendar
//
// Example:
// | new dijit.form.DateTextBox({value: new Date(2009, 0, 20)})
//
// Example:
// |
baseClass: "dijitTextBox dijitComboBox dijitDateTextBox",
popupClass: "dijit.Calendar",
_selector: "date",
// value: Date
// The value of this widget as a JavaScript Date object, with only year/month/day specified.
// If specified in markup, use the format specified in `dojo.date.stamp.fromISOString`.
// set("value", ...) accepts either a Date object or a string.
value: new Date("") // value.toString()="NaN"
}
);
}
if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.Form"] = true;
dojo.provide("dijit.form.Form");
dojo.declare(
"dijit.form.Form",
[dijit._Widget, dijit._Templated, dijit.form._FormMixin, dijit.layout._ContentPaneResizeMixin],
{
// summary:
// Widget corresponding to HTML form tag, for validation and serialization
//
// example:
// |
// | myObj = {name: "John Doe"};
// | dijit.byId('myForm').set('value', myObj);
// |
// | myObj=dijit.byId('myForm').get('value');
// HTML ",
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
action: "",
method: "",
encType: "",
"accept-charset": "",
accept: "",
target: ""
}),
postMixInProperties: function(){
// Setup name=foo string to be referenced from the template (but only if a name has been specified)
// Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
this.inherited(arguments);
},
execute: function(/*Object*/ formContents){
// summary:
// Deprecated: use submit()
// tags:
// deprecated
},
onExecute: function(){
// summary:
// Deprecated: use onSubmit()
// tags:
// deprecated
},
_setEncTypeAttr: function(/*String*/ value){
this.encType = value;
dojo.attr(this.domNode, "encType", value);
if(dojo.isIE){ this.domNode.encoding = value; }
},
postCreate: function(){
// IE tries to hide encType
// TODO: remove in 2.0, no longer necessary with data-dojo-params
if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
var item = this.srcNodeRef.attributes.getNamedItem('encType');
if(item && !item.specified && (typeof item.value == "string")){
this.set('encType', item.value);
}
}
this.inherited(arguments);
},
reset: function(/*Event?*/ e){
// summary:
// restores all widget values back to their init values,
// calls onReset() which can cancel the reset by returning false
// create fake event so we can know if preventDefault() is called
var faux = {
returnValue: true, // the IE way
preventDefault: function(){ // not IE
this.returnValue = false;
},
stopPropagation: function(){},
currentTarget: e ? e.target : this.domNode,
target: e ? e.target : this.domNode
};
// if return value is not exactly false, and haven't called preventDefault(), then reset
if(!(this.onReset(faux) === false) && faux.returnValue){
this.inherited(arguments, []);
}
},
onReset: function(/*Event?*/ e){
// summary:
// Callback when user resets the form. This method is intended
// to be over-ridden. When the `reset` method is called
// programmatically, the return value from `onReset` is used
// to compute whether or not resetting should proceed
// tags:
// callback
return true; // Boolean
},
_onReset: function(e){
this.reset(e);
dojo.stopEvent(e);
return false;
},
_onSubmit: function(e){
var fp = dijit.form.Form.prototype;
// TODO: remove this if statement beginning with 2.0
if(this.execute != fp.execute || this.onExecute != fp.onExecute){
dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
this.onExecute();
this.execute(this.getValues());
}
if(this.onSubmit(e) === false){ // only exactly false stops submit
dojo.stopEvent(e);
}
},
onSubmit: function(/*Event?*/ e){
// summary:
// Callback when user submits the form.
// description:
// This method is intended to be over-ridden, but by default it checks and
// returns the validity of form elements. When the `submit`
// method is called programmatically, the return value from
// `onSubmit` is used to compute whether or not submission
// should proceed
// tags:
// extension
return this.isValid(); // Boolean
},
submit: function(){
// summary:
// programmatically submit form if and only if the `onSubmit` returns true
if(!(this.onSubmit() === false)){
this.containerNode.submit();
}
}
}
);
}
if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.NumberTextBox"] = true;
dojo.provide("dijit.form.NumberTextBox");
/*=====
dojo.declare(
"dijit.form.NumberTextBox.__Constraints",
[dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
// summary:
// Specifies both the rules on valid/invalid values (minimum, maximum,
// number of required decimal places), and also formatting options for
// displaying the value when the field is not focused.
// example:
// Minimum/maximum:
// To specify a field between 0 and 120:
// | {min:0,max:120}
// To specify a field that must be an integer:
// | {fractional:false}
// To specify a field where 0 to 3 decimal places are allowed on input:
// | {places:'0,3'}
});
=====*/
dojo.declare("dijit.form.NumberTextBoxMixin",
null,
{
// summary:
// A mixin for all number textboxes
// tags:
// protected
// Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
// than a straight regexp to deal with locale (plus formatting options too?)
regExpGen: dojo.number.regexp,
/*=====
// constraints: dijit.form.NumberTextBox.__Constraints
// Despite the name, this parameter specifies both constraints on the input
// (including minimum/maximum allowed values) as well as
// formatting options like places (the number of digits to display after
// the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
constraints: {},
======*/
// value: Number
// The value of this NumberTextBox as a Javascript Number (i.e., not a String).
// If the displayed value is blank, the value is NaN, and if the user types in
// an gibberish value (like "hello world"), the value is undefined
// (i.e. get('value') returns undefined).
//
// Symmetrically, set('value', NaN) will clear the displayed value,
// whereas set('value', undefined) will have no effect.
value: NaN,
// editOptions: [protected] Object
// Properties to mix into constraints when the value is being edited.
// This is here because we edit the number in the format "12345", which is
// different than the display value (ex: "12,345")
editOptions: { pattern: '#.######' },
/*=====
_formatter: function(value, options){
// summary:
// _formatter() is called by format(). It's the base routine for formatting a number,
// as a string, for example converting 12345 into "12,345".
// value: Number
// The number to be converted into a string.
// options: dojo.number.__FormatOptions?
// Formatting options
// tags:
// protected extension
return "12345"; // String
},
=====*/
_formatter: dojo.number.format,
_setConstraintsAttr: function(/*Object*/ constraints){
var places = typeof constraints.places == "number"? constraints.places : 0;
if(places){ places++; } // decimal rounding errors take away another digit of precision
if(typeof constraints.max != "number"){
constraints.max = 9 * Math.pow(10, 15-places);
}
if(typeof constraints.min != "number"){
constraints.min = -9 * Math.pow(10, 15-places);
}
this.inherited(arguments, [ constraints ]);
if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
this.set('value', this.value);
}
},
_onFocus: function(){
if(this.disabled){ return; }
var val = this.get('value');
if(typeof val == "number" && !isNaN(val)){
var formattedValue = this.format(val, this.constraints);
if(formattedValue !== undefined){
this.textbox.value = formattedValue;
}
}
this.inherited(arguments);
},
format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
// summary:
// Formats the value as a Number, according to constraints.
// tags:
// protected
var formattedValue = String(value);
if(typeof value != "number"){ return formattedValue; }
if(isNaN(value)){ return ""; }
// check for exponential notation that dojo.number.format chokes on
if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
return formattedValue;
}
if(this.editOptions && this._focused){
constraints = dojo.mixin({}, constraints, this.editOptions);
}
return this._formatter(value, constraints);
},
/*=====
_parser: function(value, constraints){
// summary:
// Parses the string value as a Number, according to constraints.
// value: String
// String representing a number
// constraints: dojo.number.__ParseOptions
// Formatting options
// tags:
// protected
return 123.45; // Number
},
=====*/
_parser: dojo.number.parse,
parse: function(/*String*/ value, /*dojo.number.__FormatOptions*/ constraints){
// summary:
// Replacable function to convert a formatted string to a number value
// tags:
// protected extension
var v = this._parser(value, dojo.mixin({}, constraints, (this.editOptions && this._focused) ? this.editOptions : {}));
if(this.editOptions && this._focused && isNaN(v)){
v = this._parser(value, constraints); // parse w/o editOptions: not technically needed but is nice for the user
}
return v;
},
_getDisplayedValueAttr: function(){
var v = this.inherited(arguments);
return isNaN(v) ? this.textbox.value : v;
},
filter: function(/*Number*/ value){
// summary:
// This is called with both the display value (string), and the actual value (a number).
// When called with the actual value it does corrections so that '' etc. are represented as NaN.
// Otherwise it dispatches to the superclass's filter() method.
//
// See `dijit.form.TextBox.filter` for more details.
return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // set('value', null||''||undefined) should fire onChange(NaN)
},
serialize: function(/*Number*/ value, /*Object?*/ options){
// summary:
// Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
// tags:
// protected
return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
},
_setBlurValue: function(){
var val = dojo.hitch(dojo.mixin({}, this, { _focused: true }), "get")('value'); // parse with editOptions
this._setValueAttr(val, true);
},
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Hook so set('value', ...) works.
if(value !== undefined && formattedValue === undefined){
formattedValue = String(value);
if(typeof value == "number"){
if(isNaN(value)){ formattedValue = '' }
// check for exponential notation that dojo.number.format chokes on
else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
formattedValue = undefined; // lets format comnpute a real string value
}
}else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
formattedValue = '';
value = NaN;
}else{ // non-numeric values
value = undefined;
}
}
this.inherited(arguments, [value, priorityChange, formattedValue]);
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// Returns Number, NaN for '', or undefined for unparsable text
var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
// If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
// returns NaN; this if() branch converts the return value to undefined.
// Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
// A blank displayed value is still returned as NaN.
if(isNaN(v) && this.textbox.value !== ''){
if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value) && (new RegExp("^"+dojo.number._realNumberRegexp(dojo.mixin({}, this.constraints))+"$").test(this.textbox.value))){ // check for exponential notation that parse() rejected (erroneously?)
var n = Number(this.textbox.value);
return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
}else{
return undefined; // gibberish
}
}else{
return v; // Number or NaN for ''
}
},
isValid: function(/*Boolean*/ isFocused){
// Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
// it may not be formatted according to the regExp vaidation rules
if(!this._focused || this._isEmpty(this.textbox.value)){
return this.inherited(arguments);
}else{
var v = this.get('value');
if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
return true; // valid exponential number in range
}else{
return this.inherited(arguments);
}
}else{
return false;
}
}
}
}
);
dojo.declare("dijit.form.NumberTextBox",
[dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
{
// summary:
// A TextBox for entering numbers, with formatting and range checking
// description:
// NumberTextBox is a textbox for entering and displaying numbers, supporting
// the following main features:
//
// 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
// a number rather than a random string)
// 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
// depending on locale).
// 3. Separate modes for editing the value and displaying it, specifically that
// the thousands separator character (typically comma) disappears when editing
// but reappears after the field is blurred.
// 4. Formatting and constraints regarding the number of places (digits after the decimal point)
// allowed on input, and number of places displayed when blurred (see `constraints` parameter).
baseClass: "dijitTextBox dijitNumberTextBox"
}
);
}
if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.RadioButton"] = true;
dojo.provide("dijit.form.RadioButton");
// TODO: for 2.0, move the RadioButton code into this file
}
if(!dojo._hasResource["dijit.form.HorizontalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.HorizontalSlider"] = true;
dojo.provide("dijit.form.HorizontalSlider");
dojo.declare(
"dijit.form.HorizontalSlider",
[dijit.form._FormValueWidget, dijit._Container],
{
// summary:
// A form widget that allows one to select a value with a horizontally draggable handle
templateString: dojo.cache("dijit.form", "templates/HorizontalSlider.html", "\n"),
// Overrides FormValueWidget.value to indicate numeric value
value: 0,
// showButtons: [const] Boolean
// Show increment/decrement buttons at the ends of the slider?
showButtons: true,
// minimum:: [const] Integer
// The minimum value the slider can be set to.
minimum: 0,
// maximum: [const] Integer
// The maximum value the slider can be set to.
maximum: 100,
// discreteValues: Integer
// If specified, indicates that the slider handle has only 'discreteValues' possible positions,
// and that after dragging the handle, it will snap to the nearest possible position.
// Thus, the slider has only 'discreteValues' possible values.
//
// For example, if minimum=10, maxiumum=30, and discreteValues=3, then the slider handle has
// three possible positions, representing values 10, 20, or 30.
//
// If discreteValues is not specified or if it's value is higher than the number of pixels
// in the slider bar, then the slider handle can be moved freely, and the slider's value will be
// computed/reported based on pixel position (in this case it will likely be fractional,
// such as 123.456789).
discreteValues: Infinity,
// pageIncrement: Integer
// If discreteValues is also specified, this indicates the amount of clicks (ie, snap positions)
// that the slider handle is moved via pageup/pagedown keys.
// If discreteValues is not specified, it indicates the number of pixels.
pageIncrement: 2,
// clickSelect: Boolean
// If clicking the slider bar changes the value or not
clickSelect: true,
// slideDuration: Number
// The time in ms to take to animate the slider handle from 0% to 100%,
// when clicking the slider bar to make the handle move.
slideDuration: dijit.defaultDuration,
// Flag to _Templated (TODO: why is this here? I see no widgets in the template.)
widgetsInTemplate: true,
attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
id: ""
}),
baseClass: "dijitSlider",
// Apply CSS classes to up/down arrows and handle per mouse state
cssStateNodes: {
incrementButton: "dijitSliderIncrementButton",
decrementButton: "dijitSliderDecrementButton",
focusNode: "dijitSliderThumb"
},
_mousePixelCoord: "pageX",
_pixelCount: "w",
_startingPixelCoord: "x",
_startingPixelCount: "l",
_handleOffsetCoord: "left",
_progressPixelSize: "width",
_onKeyUp: function(/*Event*/ e){
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
this._setValueAttr(this.value, true);
},
_onKeyPress: function(/*Event*/ e){
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey || e.metaKey){ return; }
switch(e.charOrCode){
case dojo.keys.HOME:
this._setValueAttr(this.minimum, false);
break;
case dojo.keys.END:
this._setValueAttr(this.maximum, false);
break;
// this._descending === false: if ascending vertical (min on top)
// (this._descending || this.isLeftToRight()): if left-to-right horizontal or descending vertical
case ((this._descending || this.isLeftToRight()) ? dojo.keys.RIGHT_ARROW : dojo.keys.LEFT_ARROW):
case (this._descending === false ? dojo.keys.DOWN_ARROW : dojo.keys.UP_ARROW):
case (this._descending === false ? dojo.keys.PAGE_DOWN : dojo.keys.PAGE_UP):
this.increment(e);
break;
case ((this._descending || this.isLeftToRight()) ? dojo.keys.LEFT_ARROW : dojo.keys.RIGHT_ARROW):
case (this._descending === false ? dojo.keys.UP_ARROW : dojo.keys.DOWN_ARROW):
case (this._descending === false ? dojo.keys.PAGE_UP : dojo.keys.PAGE_DOWN):
this.decrement(e);
break;
default:
return;
}
dojo.stopEvent(e);
},
_onHandleClick: function(e){
if(this.disabled || this.readOnly){ return; }
if(!dojo.isIE){
// make sure you get focus when dragging the handle
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
dijit.focus(this.sliderHandle);
}
dojo.stopEvent(e);
},
_isReversed: function(){
// summary:
// Returns true if direction is from right to left
// tags:
// protected extension
return !this.isLeftToRight();
},
_onBarClick: function(e){
if(this.disabled || this.readOnly || !this.clickSelect){ return; }
dijit.focus(this.sliderHandle);
dojo.stopEvent(e);
var abspos = dojo.position(this.sliderBarContainer, true);
var pixelValue = e[this._mousePixelCoord] - abspos[this._startingPixelCoord];
this._setPixelValue(this._isReversed() ? (abspos[this._pixelCount] - pixelValue) : pixelValue, abspos[this._pixelCount], true);
this._movable.onMouseDown(e);
},
_setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean?*/ priorityChange){
if(this.disabled || this.readOnly){ return; }
pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
var count = this.discreteValues;
if(count <= 1 || count == Infinity){ count = maxPixels; }
count--;
var pixelsPerValue = maxPixels / count;
var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
this._setValueAttr((this.maximum-this.minimum)*wholeIncrements/count + this.minimum, priorityChange);
},
_setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('value', value) works.
this._set("value", value);
this.valueNode.value = value;
dijit.setWaiState(this.focusNode, "valuenow", value);
this.inherited(arguments);
var percent = (value - this.minimum) / (this.maximum - this.minimum);
var progressBar = (this._descending === false) ? this.remainingBar : this.progressBar;
var remainingBar = (this._descending === false) ? this.progressBar : this.remainingBar;
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
this._inProgressAnim.stop(true);
}
if(priorityChange && this.slideDuration > 0 && progressBar.style[this._progressPixelSize]){
// animate the slider
var _this = this;
var props = {};
var start = parseFloat(progressBar.style[this._progressPixelSize]);
var duration = this.slideDuration * (percent-start/100);
if(duration == 0){ return; }
if(duration < 0){ duration = 0 - duration; }
props[this._progressPixelSize] = { start: start, end: percent*100, units:"%" };
this._inProgressAnim = dojo.animateProperty({ node: progressBar, duration: duration,
onAnimate: function(v){ remainingBar.style[_this._progressPixelSize] = (100-parseFloat(v[_this._progressPixelSize])) + "%"; },
onEnd: function(){ delete _this._inProgressAnim; },
properties: props
})
this._inProgressAnim.play();
}else{
progressBar.style[this._progressPixelSize] = (percent*100) + "%";
remainingBar.style[this._progressPixelSize] = ((1-percent)*100) + "%";
}
},
_bumpValue: function(signedChange, /*Boolean?*/ priorityChange){
if(this.disabled || this.readOnly){ return; }
var s = dojo.getComputedStyle(this.sliderBarContainer);
var c = dojo._getContentBox(this.sliderBarContainer, s);
var count = this.discreteValues;
if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
count--;
var value = (this.value - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
if(value < 0){ value = 0; }
if(value > count){ value = count; }
value = value * (this.maximum - this.minimum) / count + this.minimum;
this._setValueAttr(value, priorityChange);
},
_onClkBumper: function(val){
if(this.disabled || this.readOnly || !this.clickSelect){ return; }
this._setValueAttr(val, true);
},
_onClkIncBumper: function(){
this._onClkBumper(this._descending === false ? this.minimum : this.maximum);
},
_onClkDecBumper: function(){
this._onClkBumper(this._descending === false ? this.maximum : this.minimum);
},
decrement: function(/*Event*/ e){
// summary:
// Decrement slider
// tags:
// private
this._bumpValue(e.charOrCode == dojo.keys.PAGE_DOWN ? -this.pageIncrement : -1);
},
increment: function(/*Event*/ e){
// summary:
// Increment slider
// tags:
// private
this._bumpValue(e.charOrCode == dojo.keys.PAGE_UP ? this.pageIncrement : 1);
},
_mouseWheeled: function(/*Event*/ evt){
// summary:
// Event handler for mousewheel where supported
dojo.stopEvent(evt);
var janky = !dojo.isMozilla;
var scroll = evt[(janky ? "wheelDelta" : "detail")] * (janky ? 1 : -1);
this._bumpValue(scroll < 0 ? -1 : 1, true); // negative scroll acts like a decrement
},
startup: function(){
if(this._started){ return; }
dojo.forEach(this.getChildren(), function(child){
if(this[child.container] != this.containerNode){
this[child.container].appendChild(child.domNode);
}
}, this);
this.inherited(arguments);
},
_typematicCallback: function(/*Number*/ count, /*Object*/ button, /*Event*/ e){
if(count == -1){
this._setValueAttr(this.value, true);
}else{
this[(button == (this._descending? this.incrementButton : this.decrementButton)) ? "decrement" : "increment"](e);
}
},
buildRendering: function(){
this.inherited(arguments);
if(this.showButtons){
this.incrementButton.style.display="";
this.decrementButton.style.display="";
}
// find any associated label element and add to slider focusnode.
var label = dojo.query('label[for="'+this.id+'"]');
if(label.length){
label[0].id = (this.id+"_label");
dijit.setWaiState(this.focusNode, "labelledby", label[0].id);
}
dijit.setWaiState(this.focusNode, "valuemin", this.minimum);
dijit.setWaiState(this.focusNode, "valuemax", this.maximum);
},
postCreate: function(){
this.inherited(arguments);
if(this.showButtons){
this._connects.push(dijit.typematic.addMouseListener(
this.decrementButton, this, "_typematicCallback", 25, 500));
this._connects.push(dijit.typematic.addMouseListener(
this.incrementButton, this, "_typematicCallback", 25, 500));
}
this.connect(this.domNode, !dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll", "_mouseWheeled");
// define a custom constructor for a SliderMover that points back to me
var mover = dojo.declare(dijit.form._SliderMover, {
widget: this
});
this._movable = new dojo.dnd.Moveable(this.sliderHandle, {mover: mover});
this._layoutHackIE7();
},
destroy: function(){
this._movable.destroy();
if(this._inProgressAnim && this._inProgressAnim.status != "stopped"){
this._inProgressAnim.stop(true);
}
this._supportingWidgets = dijit.findWidgets(this.domNode); // tells destroy about pseudo-child widgets (ruler/labels)
this.inherited(arguments);
}
});
dojo.declare("dijit.form._SliderMover",
dojo.dnd.Mover,
{
onMouseMove: function(e){
var widget = this.widget;
var abspos = widget._abspos;
if(!abspos){
abspos = widget._abspos = dojo.position(widget.sliderBarContainer, true);
widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
widget._isReversed_ = widget._isReversed();
}
var coordEvent = e.touches ? e.touches[0] : e, // if multitouch take first touch for coords
pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false);
},
destroy: function(e){
dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
var widget = this.widget;
widget._abspos = null;
widget._setValueAttr(widget.value, true);
}
});
}
if(!dojo._hasResource["dijit.form.VerticalSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.VerticalSlider"] = true;
dojo.provide("dijit.form.VerticalSlider");
dojo.declare(
"dijit.form.VerticalSlider",
dijit.form.HorizontalSlider,
{
// summary:
// A form widget that allows one to select a value with a vertically draggable handle
templateString: dojo.cache("dijit.form", "templates/VerticalSlider.html", "\n"),
_mousePixelCoord: "pageY",
_pixelCount: "h",
_startingPixelCoord: "y",
_startingPixelCount: "t",
_handleOffsetCoord: "top",
_progressPixelSize: "height",
// _descending: Boolean
// Specifies if the slider values go from high-on-top (true), or low-on-top (false)
// TODO: expose this in 1.2 - the css progress/remaining bar classes need to be reversed
_descending: true,
_isReversed: function(){
// summary:
// Overrides HorizontalSlider._isReversed.
// Indicates if values are high on top (with low numbers on the bottom).
return this._descending;
}
});
}
if(!dojo._hasResource["dijit.form.HorizontalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.HorizontalRule"] = true;
dojo.provide("dijit.form.HorizontalRule");
dojo.declare("dijit.form.HorizontalRule", [dijit._Widget, dijit._Templated],
{
// summary:
// Hash marks for `dijit.form.HorizontalSlider`
templateString: '
',
// count: Integer
// Number of hash marks to generate
count: 3,
// container: String
// For HorizontalSlider, this is either "topDecoration" or "bottomDecoration",
// and indicates whether this rule goes above or below the slider.
container: "containerNode",
// ruleStyle: String
// CSS style to apply to individual hash marks
ruleStyle: "",
_positionPrefix: '
',
_genHTML: function(pos, ndx){
return this._positionPrefix + pos + this._positionSuffix + this.ruleStyle + this._suffix;
},
// _isHorizontal: [protected extension] Boolean
// VerticalRule will override this...
_isHorizontal: true,
buildRendering: function(){
this.inherited(arguments);
var innerHTML;
if(this.count == 1){
innerHTML = this._genHTML(50, 0);
}else{
var i;
var interval = 100 / (this.count-1);
if(!this._isHorizontal || this.isLeftToRight()){
innerHTML = this._genHTML(0, 0);
for(i=1; i < this.count-1; i++){
innerHTML += this._genHTML(interval*i, i);
}
innerHTML += this._genHTML(100, this.count-1);
}else{
innerHTML = this._genHTML(100, 0);
for(i=1; i < this.count-1; i++){
innerHTML += this._genHTML(100-interval*i, i);
}
innerHTML += this._genHTML(0, this.count-1);
}
}
this.domNode.innerHTML = innerHTML;
}
});
}
if(!dojo._hasResource["dijit.form.VerticalRule"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.VerticalRule"] = true;
dojo.provide("dijit.form.VerticalRule");
dojo.declare("dijit.form.VerticalRule", dijit.form.HorizontalRule,
{
// summary:
// Hash marks for the `dijit.form.VerticalSlider`
templateString: '
',
_positionPrefix: '
',
// labelStyle: String
// CSS style to apply to individual text labels
labelStyle: "",
// labels: String[]?
// Array of text labels to render - evenly spaced from left-to-right or bottom-to-top.
// Alternately, minimum and maximum can be specified, to get numeric labels.
labels: [],
// numericMargin: Integer
// Number of generated numeric labels that should be rendered as '' on the ends when labels[] are not specified
numericMargin: 0,
// numericMinimum: Integer
// Leftmost label value for generated numeric labels when labels[] are not specified
minimum: 0,
// numericMaximum: Integer
// Rightmost label value for generated numeric labels when labels[] are not specified
maximum: 1,
// constraints: Object
// pattern, places, lang, et al (see dojo.number) for generated numeric labels when labels[] are not specified
constraints: {pattern:"#%"},
_positionPrefix: '',
_calcPosition: function(pos){
// summary:
// Returns the value to be used in HTML for the label as part of the left: attribute
// tags:
// protected extension
return pos;
},
_genHTML: function(pos, ndx){
return this._positionPrefix + this._calcPosition(pos) + this._positionSuffix + this.labelStyle + this._labelPrefix + this.labels[ndx] + this._suffix;
},
getLabels: function(){
// summary:
// Overridable function to return array of labels to use for this slider.
// Can specify a getLabels() method instead of a labels[] array, or min/max attributes.
// tags:
// protected extension
// if the labels array was not specified directly, then see if children were
var labels = this.labels;
if(!labels.length){
// for markup creation, labels are specified as child elements
labels = dojo.query("> li", this.srcNodeRef).map(function(node){
return String(node.innerHTML);
});
}
this.srcNodeRef.innerHTML = '';
// if the labels were not specified directly and not as children, then calculate numeric labels
if(!labels.length && this.count > 1){
var start = this.minimum;
var inc = (this.maximum - start) / (this.count-1);
for(var i=0; i < this.count; i++){
labels.push((i < this.numericMargin || i >= (this.count-this.numericMargin)) ? '' : dojo.number.format(start, this.constraints));
start += inc;
}
}
return labels;
},
postMixInProperties: function(){
this.inherited(arguments);
this.labels = this.getLabels();
this.count = this.labels.length;
}
});
}
if(!dojo._hasResource["dijit.form.VerticalRuleLabels"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.VerticalRuleLabels"] = true;
dojo.provide("dijit.form.VerticalRuleLabels");
dojo.declare("dijit.form.VerticalRuleLabels", dijit.form.HorizontalRuleLabels,
{
// summary:
// Labels for the `dijit.form.VerticalSlider`
templateString: '
',
_positionPrefix: '',
_calcPosition: function(pos){
// Overrides HorizontalRuleLabel._calcPosition()
return 100-pos;
},
// needed to prevent labels from being reversed in RTL mode
_isHorizontal: false
});
}
if(!dojo._hasResource["dijit.form.Slider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.Slider"] = true;
dojo.provide("dijit.form.Slider");
dojo.deprecated("Call require() for HorizontalSlider / VerticalRule, explicitly rather than 'dijit.form.Slider' itself", "", "2.0");
// For back-compat, remove for 2.0
}
if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.SimpleTextarea"] = true;
dojo.provide("dijit.form.SimpleTextarea");
dojo.declare("dijit.form.SimpleTextarea",
dijit.form.TextBox,
{
// summary:
// A simple textarea that degrades, and responds to
// minimal LayoutContainer usage, and works with dijit.form.Form.
// Doesn't automatically size according to input, like Textarea.
//
// example:
// |
//
// example:
// | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
baseClass: "dijitTextBox dijitTextArea",
attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
rows:"textbox", cols: "textbox"
}),
// rows: Number
// The number of rows of text.
rows: "3",
// rows: Number
// The number of characters per line.
cols: "20",
templateString: "",
postMixInProperties: function(){
// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
// TODO: parser will handle this in 2.0
if(!this.value && this.srcNodeRef){
this.value = this.srcNodeRef.value;
}
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
dojo.addClass(this.textbox, "dijitTextAreaCols");
}
},
filter: function(/*String*/ value){
// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
// as \r\n instead of just \n
if(value){
value = value.replace(/\r/g,"");
}
return this.inherited(arguments);
},
_previousValue: "",
_onInput: function(/*Event?*/ e){
// Override TextBox._onInput() to enforce maxLength restriction
if(this.maxLength){
var maxLength = parseInt(this.maxLength);
var value = this.textbox.value.replace(/\r/g,'');
var overflow = value.length - maxLength;
if(overflow > 0){
if(e){ dojo.stopEvent(e); }
var textarea = this.textbox;
if(textarea.selectionStart){
var pos = textarea.selectionStart;
var cr = 0;
if(dojo.isOpera){
cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
}
this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
textarea.setSelectionRange(pos-overflow, pos-overflow);
}else if(dojo.doc.selection){ //IE
textarea.focus();
var range = dojo.doc.selection.createRange();
// delete overflow characters
range.moveStart("character", -overflow);
range.text = '';
// show cursor
range.select();
}
}
this._previousValue = this.textbox.value;
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.form._ExpandingTextAreaMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form._ExpandingTextAreaMixin"] = true;
dojo.provide("dijit.form._ExpandingTextAreaMixin");
// module:
// dijit/form/_ExpandingTextAreaMixin
// summary:
// Mixin for textarea widgets to add auto-expanding capability
// feature detection
var needsHelpShrinking;
dojo.declare("dijit.form._ExpandingTextAreaMixin", null, {
// summary:
// Mixin for textarea widgets to add auto-expanding capability
_setValueAttr: function(){
this.inherited(arguments);
this.resize();
},
postCreate: function(){
this.inherited(arguments);
var textarea = this.textbox;
if(needsHelpShrinking == undefined){
var te = dojo.create('textarea', {rows:"5", cols:"20", value: ' ', style: {zoom:1, fontSize:"12px", height:"96px", overflow:'hidden', visibility:'hidden', position:'absolute', border:"5px solid white", margin:"0", padding:"0", boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' }}, dojo.body(), "last");
needsHelpShrinking = te.scrollHeight >= te.clientHeight;
dojo.body().removeChild(te);
}
this.connect(textarea, "onresize", "_resizeLater");
this.connect(textarea, "onfocus", "_resizeLater");
textarea.style.overflowY = "hidden";
},
startup: function(){
this.inherited(arguments);
this._resizeLater();
},
_onInput: function(e){
this.inherited(arguments);
this.resize();
},
_estimateHeight: function(){
// summary:
// Approximate the height when the textarea is invisible with the number of lines in the text.
// Fails when someone calls setValue with a long wrapping line, but the layout fixes itself when the user clicks inside so . . .
// In IE, the resize event is supposed to fire when the textarea becomes visible again and that will correct the size automatically.
//
var textarea = this.textbox;
// #rows = #newlines+1
textarea.rows = (textarea.value.match(/\n/g) || []).length + 1;
},
_resizeLater: function(){
this.defer("resize");
},
resize: function(){
// summary:
// Resizes the textarea vertically (should be called after a style/value change)
var textarea = this.textbox;
function textareaScrollHeight(){
var empty = false;
if(textarea.value === ''){
textarea.value = ' ';
empty = true;
}
var sh = textarea.scrollHeight;
if(empty){ textarea.value = ''; }
return sh;
}
if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
if(this.busyResizing){ return; }
this.busyResizing = true;
if(textareaScrollHeight() || textarea.offsetHeight){
var newH = textareaScrollHeight() + Math.max(textarea.offsetHeight - textarea.clientHeight, 0);
var newHpx = newH + "px";
if(newHpx != textarea.style.height){
textarea.style.height = newHpx;
textarea.rows = 1; // rows can act like a minHeight if not cleared
}
if(needsHelpShrinking){
var origScrollHeight = textareaScrollHeight(),
newScrollHeight = origScrollHeight,
origMinHeight = textarea.style.minHeight,
decrement = 4, // not too fast, not too slow
thisScrollHeight,
origScrollTop = textarea.scrollTop;
textarea.style.minHeight = newHpx; // maintain current height
textarea.style.height = "auto"; // allow scrollHeight to change
while(newH > 0){
textarea.style.minHeight = Math.max(newH - decrement, 4) + "px";
thisScrollHeight = textareaScrollHeight();
var change = newScrollHeight - thisScrollHeight;
newH -= change;
if(change < decrement){
break; // scrollHeight didn't shrink
}
newScrollHeight = thisScrollHeight;
decrement <<= 1;
}
textarea.style.height = newH + "px";
textarea.style.minHeight = origMinHeight;
textarea.scrollTop = origScrollTop;
}
textarea.style.overflowY = textareaScrollHeight() > textarea.clientHeight ? "auto" : "hidden";
if(textarea.style.overflowY == "hidden"){ textarea.scrollTop = 0; }
}else{
// hidden content of unknown size
this._estimateHeight();
}
this.busyResizing = false;
}
});
}
if(!dojo._hasResource["dijit.form.Textarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.form.Textarea"] = true;
dojo.provide("dijit.form.Textarea");
/*=====
var _ExpandingTextAreaMixin = dijit.form._ExpandingTextAreaMixin;
var SimpleTextarea = dijit.form.SimpleTextarea;
=====*/
// module:
// dijit/form/Textarea
// summary:
// A textarea widget that adjusts it's height according to the amount of data.
dojo.declare("dijit.form.Textarea", [dijit.form.SimpleTextarea, dijit.form._ExpandingTextAreaMixin], {
// summary:
// A textarea widget that adjusts it's height according to the amount of data.
//
// description:
// A textarea that dynamically expands/contracts (changing it's height) as
// the user types, to display all the text without requiring a scroll bar.
//
// Takes nearly all the parameters (name, value, etc.) that a vanilla textarea takes.
// Rows is not supported since this widget adjusts the height.
//
// example:
// |
// TODO: for 2.0, rename this to ExpandingTextArea, and rename SimpleTextarea to TextArea
baseClass: "dijitTextBox dijitTextArea dijitExpandingTextArea",
// Override SimpleTextArea.cols to default to width:100%, for backward compatibility
cols: "",
buildRendering: function(){
this.inherited(arguments);
// tweak textarea style to reduce browser differences
dojo.style(this.textbox, { overflowY: 'hidden', overflowX: 'auto', boxSizing: 'border-box', MsBoxSizing: 'border-box', WebkitBoxSizing: 'border-box', MozBoxSizing: 'border-box' });
}
});
}
if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.BorderContainer"] = true;
dojo.provide("dijit.layout.BorderContainer");
dojo.declare(
"dijit.layout.BorderContainer",
dijit.layout._LayoutWidget,
{
// summary:
// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
//
// description:
// A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
// that contains a child widget marked region="center" and optionally children widgets marked
// region equal to "top", "bottom", "leading", "trailing", "left" or "right".
// Children along the edges will be laid out according to width or height dimensions and may
// include optional splitters (splitter="true") to make them resizable by the user. The remaining
// space is designated for the center region.
//
// The outer size must be specified on the BorderContainer node. Width must be specified for the sides
// and height for the top and bottom, respectively. No dimensions should be specified on the center;
// it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
// "left" and "right" except that they will be reversed in right-to-left environments.
//
// For complex layouts, multiple children can be specified for a single region. In this case, the
// layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
// and which child is closer to the center (high layoutPriority). layoutPriority can also be used
// instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes.
// example:
// |
// |
header text
// |
table of contents
// |
client area
// |
// design: String
// Which design is used for the layout:
// - "headline" (default) where the top and bottom extend
// the full width of the container
// - "sidebar" where the left and right sides extend from top to bottom.
design: "headline",
// gutters: [const] Boolean
// Give each pane a border and margin.
// Margin determined by domNode.paddingLeft.
// When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
gutters: true,
// liveSplitters: [const] Boolean
// Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
liveSplitters: true,
// persist: Boolean
// Save splitter positions in a cookie.
persist: false,
baseClass: "dijitBorderContainer",
// _splitterClass: String
// Optional hook to override the default Splitter widget used by BorderContainer
_splitterClass: "dijit.layout._Splitter",
postMixInProperties: function(){
// change class name to indicate that BorderContainer is being used purely for
// layout (like LayoutContainer) rather than for pretty formatting.
if(!this.gutters){
this.baseClass += "NoGutter";
}
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
dojo.forEach(this.getChildren(), this._setupChild, this);
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget._setupChild().
var region = child.region;
if(region){
this.inherited(arguments);
dojo.addClass(child.domNode, this.baseClass+"Pane");
var ltr = this.isLeftToRight();
if(region == "leading"){ region = ltr ? "left" : "right"; }
if(region == "trailing"){ region = ltr ? "right" : "left"; }
// Create draggable splitter for resizing pane,
// or alternately if splitter=false but BorderContainer.gutters=true then
// insert dummy div just for spacing
if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
var splitter = new _Splitter({
id: child.id + "_splitter",
container: this,
child: child,
region: region,
live: this.liveSplitters
});
splitter.isSplitter = true;
child._splitterWidget = splitter;
dojo.place(splitter.domNode, child.domNode, "after");
// Splitters aren't added as Contained children, so we need to call startup explicitly
splitter.startup();
}
child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
}
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
this._layoutChildren();
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// Override _LayoutWidget.addChild().
this.inherited(arguments);
if(this._started){
this.layout(); //OPT
}
},
removeChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget.removeChild().
var region = child.region;
var splitter = child._splitterWidget
if(splitter){
splitter.destroy();
delete child._splitterWidget;
}
this.inherited(arguments);
if(this._started){
this._layoutChildren();
}
// Clean up whatever style changes we made to the child pane.
// Unclear how height and width should be handled.
dojo.removeClass(child.domNode, this.baseClass+"Pane");
dojo.style(child.domNode, {
top: "auto",
bottom: "auto",
left: "auto",
right: "auto",
position: "static"
});
dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
},
getChildren: function(){
// Override _LayoutWidget.getChildren() to only return real children, not the splitters.
return dojo.filter(this.inherited(arguments), function(widget){
return !widget.isSplitter;
});
},
// TODO: remove in 2.0
getSplitter: function(/*String*/region){
// summary:
// Returns the widget responsible for rendering the splitter associated with region
// tags:
// deprecated
return dojo.filter(this.getChildren(), function(child){
return child.region == region;
})[0]._splitterWidget;
},
resize: function(newSize, currentSize){
// Overrides _LayoutWidget.resize().
// resetting potential padding to 0px to provide support for 100% width/height + padding
// TODO: this hack doesn't respect the box model and is a temporary fix
if(!this.cs || !this.pe){
var node = this.domNode;
this.cs = dojo.getComputedStyle(node);
this.pe = dojo._getPadExtents(node, this.cs);
this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
dojo.style(node, "padding", "0px");
}
this.inherited(arguments);
},
_layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
// summary:
// This is the main routine for setting size/position of each child.
// description:
// With no arguments, measures the height of top/bottom panes, the width
// of left/right panes, and then sizes all panes accordingly.
//
// With changedRegion specified (as "left", "top", "bottom", or "right"),
// it changes that region's width/height to changedRegionSize and
// then resizes other regions that were affected.
// changedChildId:
// Id of the child which should be resized because splitter was dragged.
// changedChildSize:
// The new width/height (in pixels) to make specified child
if(!this._borderBox || !this._borderBox.h){
// We are currently hidden, or we haven't been sized by our parent yet.
// Abort. Someone will resize us later.
return;
}
// Generate list of wrappers of my children in the order that I want layoutChildren()
// to process them (i.e. from the outside to the inside)
var wrappers = dojo.map(this.getChildren(), function(child, idx){
return {
pane: child,
weight: [
child.region == "center" ? Infinity : 0,
child.layoutPriority,
(this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
idx
]
};
}, this);
wrappers.sort(function(a, b){
var aw = a.weight, bw = b.weight;
for(var i=0; i
',
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
this._factor = /top|left/.test(this.region) ? 1 : -1;
this._cookieName = this.container.id + "_" + this.region;
},
buildRendering: function(){
this.inherited(arguments);
dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
if(this.container.persist){
// restore old size
var persistSize = dojo.cookie(this._cookieName);
if(persistSize){
this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
}
}
},
_computeMaxSize: function(){
// summary:
// Return the maximum size that my corresponding pane can be set to
var dim = this.horizontal ? 'h' : 'w',
childSize = dojo.marginBox(this.child.domNode)[dim],
center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
spaceAvailable = dojo.marginBox(center.domNode)[dim]; // can expand until center is crushed to 0
return Math.min(this.child.maxSize, childSize + spaceAvailable);
},
_startDrag: function(e){
if(!this.cover){
this.cover = dojo.doc.createElement('div');
dojo.addClass(this.cover, "dijitSplitterCover");
dojo.place(this.cover, this.child.domNode, "after");
}
dojo.addClass(this.cover, "dijitSplitterCoverActive");
// Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
if(this.fake){ dojo.destroy(this.fake); }
if(!(this._resize = this.live)){ //TODO: disable live for IE6?
// create fake splitter to display at old position while we drag
(this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
dojo.addClass(this.domNode, "dijitSplitterShadow");
dojo.place(this.fake, this.domNode, "after");
}
dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
if(this.fake){
dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
}
//Performance: load data info local vars for onmousevent function closure
var factor = this._factor,
isHorizontal = this.horizontal,
axis = isHorizontal ? "pageY" : "pageX",
pageStart = e[axis],
splitterStyle = this.domNode.style,
dim = isHorizontal ? 'h' : 'w',
childStart = dojo.marginBox(this.child.domNode)[dim],
max = this._computeMaxSize(),
min = this.child.minSize || 20,
region = this.region,
splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
splitterStart = parseInt(splitterStyle[splitterAttr], 10),
resize = this._resize,
layoutFunc = dojo.hitch(this.container, "_layoutChildren", this.child.id),
de = dojo.doc;
this._handlers = (this._handlers || []).concat([
dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
var delta = e[axis] - pageStart,
childSize = factor * delta + childStart,
boundChildSize = Math.max(Math.min(childSize, max), min);
if(resize || forceResize){
layoutFunc(boundChildSize);
}
// TODO: setting style directly (usually) sets content box size, need to set margin box size
splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
}),
dojo.connect(de, "ondragstart", dojo.stopEvent),
dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
dojo.connect(de, "onmouseup", this, "_stopDrag")
]);
dojo.stopEvent(e);
},
_onMouse: function(e){
var o = (e.type == "mouseover" || e.type == "mouseenter");
dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
},
_stopDrag: function(e){
try{
if(this.cover){
dojo.removeClass(this.cover, "dijitSplitterCoverActive");
}
if(this.fake){ dojo.destroy(this.fake); }
dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter"
+ (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
this._drag(e); //TODO: redundant with onmousemove?
this._drag(e, true);
}finally{
this._cleanupHandlers();
delete this._drag;
}
if(this.container.persist){
dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
}
},
_cleanupHandlers: function(){
dojo.forEach(this._handlers, dojo.disconnect);
delete this._handlers;
},
_onKeyPress: function(/*Event*/ e){
// should we apply typematic to this?
this._resize = true;
var horizontal = this.horizontal;
var tick = 1;
var dk = dojo.keys;
switch(e.charOrCode){
case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
tick *= -1;
// break;
case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
break;
default:
// this.inherited(arguments);
return;
}
var childSize = dojo._getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
dojo.stopEvent(e);
},
destroy: function(){
this._cleanupHandlers();
delete this.child;
delete this.container;
delete this.cover;
delete this.fake;
this.inherited(arguments);
}
});
dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
{
// summary:
// Just a spacer div to separate side pane from center pane.
// Basically a trick to lookup the gutter/splitter width from the theme.
// description:
// Instantiated by `dijit.layout.BorderContainer`. Users should not
// create directly.
// tags:
// private
templateString: '
',
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
},
buildRendering: function(){
this.inherited(arguments);
dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
}
});
}
if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.StackController"] = true;
dojo.provide("dijit.layout.StackController");
dojo.declare(
"dijit.layout.StackController",
[dijit._Widget, dijit._Templated, dijit._Container],
{
// summary:
// Set of buttons to select a page in a page list.
// description:
// Monitors the specified StackContainer, and whenever a page is
// added, deleted, or selected, updates itself accordingly.
templateString: " ",
// containerId: [const] String
// The id of the page container that I point to
containerId: "",
// buttonWidget: [const] String
// The name of the button widget to create to correspond to each page
buttonWidget: "dijit.layout._StackButton",
constructor: function(){
this.pane2button = {}; // mapping from pane id to buttons
this.pane2connects = {}; // mapping from pane id to this.connect() handles
this.pane2watches = {}; // mapping from pane id to watch() handles
},
buildRendering: function(){
this.inherited(arguments);
dijit.setWaiRole(this.domNode, "tablist"); // TODO: unneeded? it's in template above.
},
postCreate: function(){
this.inherited(arguments);
// Listen to notifications from StackContainer
this.subscribe(this.containerId+"-startup", "onStartup");
this.subscribe(this.containerId+"-addChild", "onAddChild");
this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
this.subscribe(this.containerId+"-selectChild", "onSelectChild");
this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
},
onStartup: function(/*Object*/ info){
// summary:
// Called after StackContainer has finished initializing
// tags:
// private
dojo.forEach(info.children, this.onAddChild, this);
if(info.selected){
// Show button corresponding to selected pane (unless selected
// is null because there are no panes)
this.onSelectChild(info.selected);
}
},
destroy: function(){
for(var pane in this.pane2button){
this.onRemoveChild(dijit.byId(pane));
}
this.inherited(arguments);
},
onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
// summary:
// Called whenever a page is added to the container.
// Create button corresponding to the page.
// tags:
// private
// create an instance of the button widget
var cls = dojo.getObject(this.buttonWidget);
var button = new cls({
id: this.id + "_" + page.id,
label: page.title,
dir: page.dir,
lang: page.lang,
showLabel: page.showTitle,
iconClass: page.iconClass,
closeButton: page.closable,
title: page.tooltip
});
dijit.setWaiState(button.focusNode,"selected", "false");
// map from page attribute to corresponding tab button attribute
var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
// watch() so events like page title changes are reflected in tab button
this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
return page.watch(pageAttr, function(name, oldVal, newVal){
button.set(buttonAttrList[idx], newVal);
});
});
// connections so that clicking a tab button selects the corresponding page
this.pane2connects[page.id] = [
this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
];
this.addChild(button, insertIndex);
this.pane2button[page.id] = button;
page.controlButton = button; // this value might be overwritten if two tabs point to same container
if(!this._currentChild){ // put the first child into the tab order
button.focusNode.setAttribute("tabIndex", "0");
dijit.setWaiState(button.focusNode, "selected", "true");
this._currentChild = page;
}
// make sure all tabs have the same length
if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
this._rectifyRtlTabList();
}
},
onRemoveChild: function(/*dijit._Widget*/ page){
// summary:
// Called whenever a page is removed from the container.
// Remove the button corresponding to the page.
// tags:
// private
if(this._currentChild === page){ this._currentChild = null; }
// disconnect/unwatch connections/watches related to page being removed
dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
delete this.pane2connects[page.id];
dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
delete this.pane2watches[page.id];
var button = this.pane2button[page.id];
if(button){
this.removeChild(button);
delete this.pane2button[page.id];
button.destroy();
}
delete page.controlButton;
},
onSelectChild: function(/*dijit._Widget*/ page){
// summary:
// Called when a page has been selected in the StackContainer, either by me or by another StackController
// tags:
// private
if(!page){ return; }
if(this._currentChild){
var oldButton=this.pane2button[this._currentChild.id];
oldButton.set('checked', false);
dijit.setWaiState(oldButton.focusNode, "selected", "false");
oldButton.focusNode.setAttribute("tabIndex", "-1");
}
var newButton=this.pane2button[page.id];
newButton.set('checked', true);
dijit.setWaiState(newButton.focusNode, "selected", "true");
this._currentChild = page;
newButton.focusNode.setAttribute("tabIndex", "0");
var container = dijit.byId(this.containerId);
dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
},
onButtonClick: function(/*dijit._Widget*/ page){
// summary:
// Called whenever one of my child buttons is pressed in an attempt to select a page
// tags:
// private
var container = dijit.byId(this.containerId);
container.selectChild(page);
},
onCloseButtonClick: function(/*dijit._Widget*/ page){
// summary:
// Called whenever one of my child buttons [X] is pressed in an attempt to close a page
// tags:
// private
var container = dijit.byId(this.containerId);
container.closeChild(page);
if(this._currentChild){
var b = this.pane2button[this._currentChild.id];
if(b){
dijit.focus(b.focusNode || b.domNode);
}
}
},
// TODO: this is a bit redundant with forward, back api in StackContainer
adjacent: function(/*Boolean*/ forward){
// summary:
// Helper for onkeypress to find next/previous button
// tags:
// private
if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
// find currently focused button in children array
var children = this.getChildren();
var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
// pick next button to focus on
var offset = forward ? 1 : children.length - 1;
return children[ (current + offset) % children.length ]; // dijit._Widget
},
onkeypress: function(/*Event*/ e){
// summary:
// Handle keystrokes on the page list, for advancing to next/previous button
// and closing the current page if the page is closable.
// tags:
// private
if(this.disabled || e.altKey ){ return; }
var forward = null;
if(e.ctrlKey || !e._djpage){
var k = dojo.keys;
switch(e.charOrCode){
case k.LEFT_ARROW:
case k.UP_ARROW:
if(!e._djpage){ forward = false; }
break;
case k.PAGE_UP:
if(e.ctrlKey){ forward = false; }
break;
case k.RIGHT_ARROW:
case k.DOWN_ARROW:
if(!e._djpage){ forward = true; }
break;
case k.PAGE_DOWN:
if(e.ctrlKey){ forward = true; }
break;
case k.HOME:
case k.END:
var children = this.getChildren();
if(children && children.length){
children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
}
dojo.stopEvent(e);
break;
case k.DELETE:
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
dojo.stopEvent(e);
break;
default:
if(e.ctrlKey){
if(e.charOrCode === k.TAB){
this.adjacent(!e.shiftKey).onClick();
dojo.stopEvent(e);
}else if(e.charOrCode == "w"){
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
dojo.stopEvent(e); // avoid browser tab closing.
}
}
}
// handle next/previous page navigation (left/right arrow, etc.)
if(forward !== null){
this.adjacent(forward).onClick();
dojo.stopEvent(e);
}
}
},
onContainerKeyPress: function(/*Object*/ info){
// summary:
// Called when there was a keypress on the container
// tags:
// private
info.e._djpage = info.page;
this.onkeypress(info.e);
}
});
dojo.declare("dijit.layout._StackButton",
dijit.form.ToggleButton,
{
// summary:
// Internal widget used by StackContainer.
// description:
// The button-like or tab-like object you click to select or delete a page
// tags:
// private
// Override _FormWidget.tabIndex.
// StackContainer buttons are not in the tab order by default.
// Probably we should be calling this.startupKeyNavChildren() instead.
tabIndex: "-1",
buildRendering: function(/*Event*/ evt){
this.inherited(arguments);
dijit.setWaiRole((this.focusNode || this.domNode), "tab");
},
onClick: function(/*Event*/ evt){
// summary:
// This is for TabContainer where the tabs are rather than button,
// so need to set focus explicitly (on some browsers)
// Note that you shouldn't override this method, but you can connect to it.
dijit.focus(this.focusNode);
// ... now let StackController catch the event and tell me what to do
},
onClickCloseButton: function(/*Event*/ evt){
// summary:
// StackContainer connects to this function; if your widget contains a close button
// then clicking it should call this function.
// Note that you shouldn't override this method, but you can connect to it.
evt.stopPropagation();
}
});
}
if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.StackContainer"] = true;
dojo.provide("dijit.layout.StackContainer");
dojo.declare(
"dijit.layout.StackContainer",
dijit.layout._LayoutWidget,
{
// summary:
// A container that has multiple children, but shows only
// one child at a time
//
// description:
// A container for widgets (ContentPanes, for example) That displays
// only one Widget at a time.
//
// Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
//
// Can be base class for container, Wizard, Show, etc.
// doLayout: Boolean
// If true, change the size of my currently displayed child to match my size
doLayout: true,
// persist: Boolean
// Remembers the selected child across sessions
persist: false,
baseClass: "dijitStackContainer",
/*=====
// selectedChildWidget: [readonly] dijit._Widget
// References the currently selected child widget, if any.
// Adjust selected child with selectChild() method.
selectedChildWidget: null,
=====*/
buildRendering: function(){
this.inherited(arguments);
dojo.addClass(this.domNode, "dijitLayoutContainer");
dijit.setWaiRole(this.containerNode, "tabpanel");
},
postCreate: function(){
this.inherited(arguments);
this.connect(this.domNode, "onkeypress", this._onKeyPress);
},
startup: function(){
if(this._started){ return; }
var children = this.getChildren();
// Setup each page panel to be initially hidden
dojo.forEach(children, this._setupChild, this);
// Figure out which child to initially display, defaulting to first one
if(this.persist){
this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
}else{
dojo.some(children, function(child){
if(child.selected){
this.selectedChildWidget = child;
}
return child.selected;
}, this);
}
var selected = this.selectedChildWidget;
if(!selected && children[0]){
selected = this.selectedChildWidget = children[0];
selected.selected = true;
}
// Publish information about myself so any StackControllers can initialize.
// This needs to happen before this.inherited(arguments) so that for
// TabContainer, this._contentBox doesn't include the space for the tab labels.
dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
// Startup each child widget, and do initial layout like setting this._contentBox,
// then calls this.resize() which does the initial sizing on the selected child.
this.inherited(arguments);
},
resize: function(){
// Resize is called when we are first made visible (it's called from startup()
// if we are initially visible). If this is the first time we've been made
// visible then show our first child.
if(!this._hasBeenShown){
this._hasBeenShown = true;
var selected = this.selectedChildWidget;
if(selected){
this._showChild(selected);
}
}
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
// Overrides _LayoutWidget._setupChild()
this.inherited(arguments);
dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
// remove the title attribute so it doesn't show up when i hover
// over a node
child.domNode.title = "";
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// Overrides _Container.addChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
dojo.publish(this.id+"-addChild", [child, insertIndex]);
// in case the tab titles have overflowed from one line to two lines
// (or, if this if first child, from zero lines to one line)
// TODO: w/ScrollingTabController this is no longer necessary, although
// ScrollTabController.resize() does need to get called to show/hide
// the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
this.layout();
// if this is the first child, then select it
if(!this.selectedChildWidget){
this.selectChild(child);
}
}
},
removeChild: function(/*dijit._Widget*/ page){
// Overrides _Container.removeChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
// this will notify any tablists to remove a button; do this first because it may affect sizing
dojo.publish(this.id + "-removeChild", [page]);
}
// If we are being destroyed than don't run the code below (to select another page), because we are deleting
// every page one by one
if(this._beingDestroyed){ return; }
// Select new page to display, also updating TabController to show the respective tab.
// Do this before layout call because it can affect the height of the TabController.
if(this.selectedChildWidget === page){
this.selectedChildWidget = undefined;
if(this._started){
var children = this.getChildren();
if(children.length){
this.selectChild(children[0]);
}
}
}
if(this._started){
// In case the tab titles now take up one line instead of two lines
// (note though that ScrollingTabController never overflows to multiple lines),
// or the height has changed slightly because of addition/removal of tab which close icon
this.layout();
}
},
selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
// summary:
// Show the given widget (which must be one of my children)
// page:
// Reference to child widget or id of child widget
page = dijit.byId(page);
if(this.selectedChildWidget != page){
// Deselect old page and select new one
var d = this._transition(page, this.selectedChildWidget, animate);
this._set("selectedChildWidget", page);
dojo.publish(this.id+"-selectChild", [page]);
if(this.persist){
dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
// summary:
// Hide the old widget and display the new widget.
// Subclasses should override this.
// tags:
// protected extension
if(oldWidget){
this._hideChild(oldWidget);
}
var d = this._showChild(newWidget);
// Size the new widget, in case this is the first time it's being shown,
// or I have been resized since the last time it was shown.
// Note that page must be visible for resizing to work.
if(newWidget.resize){
if(this.doLayout){
newWidget.resize(this._containerContentBox || this._contentBox);
}else{
// the child should pick it's own size but we still need to call resize()
// (with no arguments) to let the widget lay itself out
newWidget.resize();
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_adjacent: function(/*Boolean*/ forward){
// summary:
// Gets the next/previous child widget in this container from the current selection.
var children = this.getChildren();
var index = dojo.indexOf(children, this.selectedChildWidget);
index += forward ? 1 : children.length - 1;
return children[ index % children.length ]; // dijit._Widget
},
forward: function(){
// summary:
// Advance to next page.
return this.selectChild(this._adjacent(true), true);
},
back: function(){
// summary:
// Go back to previous page.
return this.selectChild(this._adjacent(false), true);
},
_onKeyPress: function(e){
dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
}
},
_showChild: function(/*dijit._Widget*/ page){
// summary:
// Show the specified child by changing it's CSS, and call _onShow()/onShow() so
// it can do any updates it needs regarding loading href's etc.
// returns:
// Promise that fires when page has finished showing, or true if there's no href
var children = this.getChildren();
page.isFirstChild = (page == children[0]);
page.isLastChild = (page == children[children.length-1]);
page._set("selected", true);
dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
return page._onShow() || true;
},
_hideChild: function(/*dijit._Widget*/ page){
// summary:
// Hide the specified child by changing it's CSS, and call _onHide() so
// it's notified.
page._set("selected", false);
dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
page.onHide();
},
closeChild: function(/*dijit._Widget*/ page){
// summary:
// Callback when user clicks the [X] to remove a page.
// If onClose() returns true then remove and destroy the child.
// tags:
// private
var remove = page.onClose(this, page);
if(remove){
this.removeChild(page);
// makes sure we can clean up executeScripts in ContentPane onUnLoad
page.destroyRecursive();
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
dojo.forEach(this.getChildren(), function(child){
this.removeChild(child);
child.destroyRecursive(preserveDom);
}, this);
}
});
// For back-compat, remove for 2.0
// These arguments can be specified for the children of a StackContainer.
// Since any widget can be specified as a StackContainer child, mix them
// into the base widget class. (This is a hack, but it's effective.)
dojo.extend(dijit._Widget, {
// selected: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// Specifies that this widget should be the initially displayed pane.
// Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
selected: false,
// closable: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
closable: false,
// iconClass: String
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// CSS Class specifying icon to use in label associated with this pane.
iconClass: "",
// showTitle: Boolean
// Parameter for children of `dijit.layout.StackContainer` or subclasses.
// When true, display title of this widget as tab label etc., rather than just using
// icon specified in iconClass
showTitle: true
});
}
if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout._TabContainerBase"] = true;
dojo.provide("dijit.layout._TabContainerBase");
dojo.declare("dijit.layout._TabContainerBase",
[dijit.layout.StackContainer, dijit._Templated],
{
// summary:
// Abstract base class for TabContainer. Must define _makeController() to instantiate
// and return the widget that displays the tab labels
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// tabPosition: String
// Defines where tabs go relative to tab content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
baseClass: "dijitTabContainer",
// tabStrip: [const] Boolean
// Defines whether the tablist gets an extra class for layouting, putting a border/shading
// around the set of tabs. Not supported by claro theme.
tabStrip: false,
// nested: [const] Boolean
// If true, use styling for a TabContainer nested inside another TabContainer.
// For tundra etc., makes tabs look like links, and hides the outer
// border since the outer TabContainer already has a border.
nested: false,
templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "\n"),
postMixInProperties: function(){
// set class name according to tab position, ex: dijitTabContainerTop
this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
// Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
this.tablist = this._makeController(this.tablistNode);
if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
if(this.nested){
/* workaround IE's lack of support for "a > b" selectors by
* tagging each node in the template.
*/
dojo.addClass(this.domNode, "dijitTabContainerNested");
dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
}else{
dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
}
},
_setupChild: function(/*dijit._Widget*/ tab){
// Overrides StackContainer._setupChild().
dojo.addClass(tab.domNode, "dijitTabPane");
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
// wire up the tablist and its tabs
this.tablist.startup();
this.inherited(arguments);
},
layout: function(){
// Overrides StackContainer.layout().
// Configure the content pane to take up all the space except for where the tabs are
if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
var sc = this.selectedChildWidget;
if(this.doLayout){
// position and size the titles and the container node
var titleAlign = this.tabPosition.replace(/-h/, "");
this.tablist.layoutAlign = titleAlign;
var children = [this.tablist, {
domNode: this.tablistSpacer,
layoutAlign: titleAlign
}, {
domNode: this.containerNode,
layoutAlign: "client"
}];
dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
// Compute size to make each of my children.
// children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
if(sc && sc.resize){
sc.resize(this._containerContentBox);
}
}else{
// just layout the tab controller, so it can position left/right buttons etc.
if(this.tablist.resize){
//make the tabs zero width so that they don't interfere with width calc, then reset
var s = this.tablist.domNode.style;
s.width="0";
var width = dojo.contentBox(this.domNode).w;
s.width="";
this.tablist.resize({w: width});
}
// and call resize() on the selected pane just to tell it that it's been made visible
if(sc && sc.resize){
sc.resize();
}
}
},
destroy: function(){
if(this.tablist){
this.tablist.destroy();
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.TabController"] = true;
dojo.provide("dijit.layout.TabController");
// Menu is used for an accessible close button, would be nice to have a lighter-weight solution
dojo.declare("dijit.layout.TabController",
dijit.layout.StackController,
{
// summary:
// Set of tabs (the things with titles and a close button, that you click to show a tab panel).
// Used internally by `dijit.layout.TabContainer`.
// description:
// Lets the user select the currently shown pane in a TabContainer or StackContainer.
// TabController also monitors the TabContainer, and whenever a pane is
// added or deleted updates itself accordingly.
// tags:
// private
templateString: "
",
// tabPosition: String
// Defines where tabs go relative to the content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
// buttonWidget: String
// The name of the tab widget to create to correspond to each page
buttonWidget: "dijit.layout._TabButton",
_rectifyRtlTabList: function(){
// summary:
// For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
if(0 >= this.tabPosition.indexOf('-h')){ return; }
if(!this.pane2button){ return; }
var maxWidth = 0;
for(var pane in this.pane2button){
var ow = this.pane2button[pane].innerDiv.scrollWidth;
maxWidth = Math.max(maxWidth, ow);
}
//unify the length of all the tabs
for(pane in this.pane2button){
this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
}
}
});
dojo.declare("dijit.layout._TabButton",
dijit.layout._StackButton,
{
// summary:
// A tab (the thing you click to select a pane).
// description:
// Contains the title of the pane, and optionally a close-button to destroy the pane.
// This is an internal widget and should not be instantiated directly.
// tags:
// private
// baseClass: String
// The CSS class applied to the domNode.
baseClass: "dijitTab",
// Apply dijitTabCloseButtonHover when close button is hovered
cssStateNodes: {
closeNode: "dijitTabCloseButton"
},
templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "\n
\n
\n \t
\n\t\t
\n\t\t
\n\t\t
\n\t\t [x] \n\t\t\t
\n
\n
\n
\n"),
// Override _FormWidget.scrollOnFocus.
// Don't scroll the whole tab container into view when the button is focused.
scrollOnFocus: false,
buildRendering: function(){
this.inherited(arguments);
dojo.setSelectable(this.containerNode, false);
},
startup: function(){
this.inherited(arguments);
var n = this.domNode;
// Required to give IE6 a kick, as it initially hides the
// tabs until they are focused on.
setTimeout(function(){
n.className = n.className;
}, 1);
},
_setCloseButtonAttr: function(/*Boolean*/ disp){
// summary:
// Hide/show close button
this._set("closeButton", disp);
dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
this.closeNode.style.display = disp ? "" : "none";
if(disp){
var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
if(this.closeNode){
dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
}
// add context menu onto title button
var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
this._closeMenu = new dijit.Menu({
id: this.id+"_Menu",
dir: this.dir,
lang: this.lang,
targetNodeIds: [this.domNode]
});
this._closeMenu.addChild(new dijit.MenuItem({
label: _nlsResources.itemClose,
dir: this.dir,
lang: this.lang,
onClick: dojo.hitch(this, "onClickCloseButton")
}));
}else{
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
}
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// takes an HTML string.
// Inherited ToggleButton implementation will Set the label (text) of the button;
// Need to set the alt attribute of icon on tab buttons if no label displayed
this.inherited(arguments);
if(this.showLabel == false && !this.params.title){
this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
},
destroy: function(){
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
dojo.provide("dijit.layout.ScrollingTabController");
dojo.declare("dijit.layout.ScrollingTabController",
dijit.layout.TabController,
{
// summary:
// Set of tabs with left/right arrow keys and a menu to switch between tabs not
// all fitting on a single row.
// Works only for horizontal tabs (either above or below the content, not to the left
// or right).
// tags:
// private
templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "\n"),
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// tabStripClass: [const] String
// The css class to apply to the tab strip, if it is visible.
tabStripClass: "",
widgetsInTemplate: true,
// _minScroll: Number
// The distance in pixels from the edge of the tab strip which,
// if a scroll animation is less than, forces the scroll to
// go all the way to the left/right.
_minScroll: 5,
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
"class": "containerNode"
}),
buildRendering: function(){
this.inherited(arguments);
var n = this.domNode;
this.scrollNode = this.tablistWrapper;
this._initButtons();
if(!this.tabStripClass){
this.tabStripClass = "dijitTabContainer" +
this.tabPosition.charAt(0).toUpperCase() +
this.tabPosition.substr(1).replace(/-.*/, "") +
"None";
dojo.addClass(n, "tabStrip-disabled")
}
dojo.addClass(this.tablistWrapper, this.tabStripClass);
},
onStartup: function(){
this.inherited(arguments);
// Do not show the TabController until the related
// StackController has added it's children. This gives
// a less visually jumpy instantiation.
dojo.style(this.domNode, "visibility", "visible");
this._postStartup = true;
},
onAddChild: function(page, insertIndex){
this.inherited(arguments);
// changes to the tab button label or iconClass will have changed the width of the
// buttons, so do a resize
dojo.forEach(["label", "iconClass"], function(attr){
this.pane2watches[page.id].push(
this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){
if(this._postStartup && this._dim){
this.resize(this._dim);
}
}))
);
}, this);
// Increment the width of the wrapper when a tab is added
// This makes sure that the buttons never wrap.
// The value 200 is chosen as it should be bigger than most
// Tab button widths.
dojo.style(this.containerNode, "width",
(dojo.style(this.containerNode, "width") + 200) + "px");
},
onRemoveChild: function(page, insertIndex){
// null out _selectedTab because we are about to delete that dom node
var button = this.pane2button[page.id];
if(this._selectedTab === button.domNode){
this._selectedTab = null;
}
this.inherited(arguments);
},
_initButtons: function(){
// summary:
// Creates the buttons used to scroll to view tabs that
// may not be visible if the TabContainer is too narrow.
// Make a list of the buttons to display when the tab labels become
// wider than the TabContainer, and hide the other buttons.
// Also gets the total width of the displayed buttons.
this._btnWidth = 0;
this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
if((this.useMenu && btn == this._menuBtn.domNode) ||
(this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
this._btnWidth += dojo._getMarginSize(btn).w;
return true;
}else{
dojo.style(btn, "display", "none");
return false;
}
}, this);
},
_getTabsWidth: function(){
var children = this.getChildren();
if(children.length){
var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
}else{
return 0;
}
},
_enableBtn: function(width){
// summary:
// Determines if the tabs are wider than the width of the TabContainer, and
// thus that we need to display left/right/menu navigation buttons.
var tabsWidth = this._getTabsWidth();
width = width || dojo.style(this.scrollNode, "width");
return tabsWidth > 0 && width < tabsWidth;
},
resize: function(dim){
// summary:
// Hides or displays the buttons used to scroll the tab list and launch the menu
// that selects tabs.
if(this.domNode.offsetWidth == 0){
return;
}
// Save the dimensions to be used when a child is renamed.
this._dim = dim;
// Set my height to be my natural height (tall enough for one row of tab labels),
// and my content-box width based on margin-box width specified in dim parameter.
// But first reset scrollNode.height in case it was set by layoutChildren() call
// in a previous run of this method.
this.scrollNode.style.height = "auto";
this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
this._contentBox.h = this.scrollNode.offsetHeight;
dojo.contentBox(this.domNode, this._contentBox);
// Show/hide the left/right/menu navigation buttons depending on whether or not they
// are needed.
var enable = this._enableBtn(this._contentBox.w);
this._buttons.style("display", enable ? "" : "none");
// Position and size the navigation buttons and the tablist
this._leftBtn.layoutAlign = "left";
this._rightBtn.layoutAlign = "right";
this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
dijit.layout.layoutChildren(this.domNode, this._contentBox,
[this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
// set proper scroll so that selected tab is visible
if(this._selectedTab){
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var w = this.scrollNode,
sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
w.scrollLeft = sl;
}
// Enable/disabled left right buttons depending on whether or not user can scroll to left or right
this._setButtonClass(this._getScroll());
this._postResize = true;
// Return my size so layoutChildren() can use it.
// Also avoids IE9 layout glitch on browser resize when scroll buttons present
return {h: this._contentBox.h, w: dim.w};
},
_getScroll: function(){
// summary:
// Returns the current scroll of the tabs where 0 means
// "scrolled all the way to the left" and some positive number, based on #
// of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
+ (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
return sl;
},
_convertToScrollLeft: function(val){
// summary:
// Given a scroll value where 0 means "scrolled all the way to the left"
// and some positive number, based on # of pixels of possible scroll (ex: 1000)
// means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
// to achieve that scroll.
//
// This method is to adjust for RTL funniness in various browsers and versions.
if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
return val;
}else{
var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
}
},
onSelectChild: function(/*dijit._Widget*/ page){
// summary:
// Smoothly scrolls to a tab when it is selected.
var tab = this.pane2button[page.id];
if(!tab || !page){return;}
// Scroll to the selected tab, except on startup, when scrolling is handled in resize()
var node = tab.domNode;
if(this._postResize && node != this._selectedTab){
this._selectedTab = node;
var sl = this._getScroll();
if(sl > node.offsetLeft ||
sl + dojo.style(this.scrollNode, "width") <
node.offsetLeft + dojo.style(node, "width")){
this.createSmoothScroll().play();
}
}
this.inherited(arguments);
},
_getScrollBounds: function(){
// summary:
// Returns the minimum and maximum scroll setting to show the leftmost and rightmost
// tabs (respectively)
var children = this.getChildren(),
scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
tabsWidth = this._getTabsWidth();
if(children.length && tabsWidth > scrollNodeWidth){
// Scrolling should happen
return {
min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
max: this.isLeftToRight() ?
(children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
maxPossibleScroll
};
}else{
// No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
return {
min: onlyScrollPosition,
max: onlyScrollPosition
};
}
},
_getScrollForSelectedTab: function(){
// summary:
// Returns the scroll value setting so that the selected tab
// will appear in the center
var w = this.scrollNode,
n = this._selectedTab,
scrollNodeWidth = dojo.style(this.scrollNode, "width"),
scrollBounds = this._getScrollBounds();
// TODO: scroll minimal amount (to either right or left) so that
// selected tab is fully visible, and just return if it's already visible?
var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
// TODO:
// If scrolling close to the left side or right side, scroll
// all the way to the left or right. See this._minScroll.
// (But need to make sure that doesn't scroll the tab out of view...)
return pos;
},
createSmoothScroll: function(x){
// summary:
// Creates a dojo._Animation object that smoothly scrolls the tab list
// either to a fixed horizontal pixel value, or to the selected tab.
// description:
// If an number argument is passed to the function, that horizontal
// pixel position is scrolled to. Otherwise the currently selected
// tab is scrolled to.
// x: Integer?
// An optional pixel value to scroll to, indicating distance from left.
// Calculate position to scroll to
if(arguments.length > 0){
// position specified by caller, just make sure it's within bounds
var scrollBounds = this._getScrollBounds();
x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
}else{
// scroll to center the current tab
x = this._getScrollForSelectedTab();
}
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var self = this,
w = this.scrollNode,
anim = new dojo._Animation({
beforeBegin: function(){
if(this.curve){ delete this.curve; }
var oldS = w.scrollLeft,
newS = self._convertToScrollLeft(x);
anim.curve = new dojo._Line(oldS, newS);
},
onAnimate: function(val){
w.scrollLeft = val;
}
});
this._anim = anim;
// Disable/enable left/right buttons according to new scroll position
this._setButtonClass(x);
return anim; // dojo._Animation
},
_getBtnNode: function(/*Event*/ e){
// summary:
// Gets a button DOM node from a mouse click event.
// e:
// The mouse click event.
var n = e.target;
while(n && !dojo.hasClass(n, "tabStripButton")){
n = n.parentNode;
}
return n;
},
doSlideRight: function(/*Event*/ e){
// summary:
// Scrolls the menu to the right.
// e:
// The mouse click event.
this.doSlide(1, this._getBtnNode(e));
},
doSlideLeft: function(/*Event*/ e){
// summary:
// Scrolls the menu to the left.
// e:
// The mouse click event.
this.doSlide(-1,this._getBtnNode(e));
},
doSlide: function(/*Number*/ direction, /*DomNode*/ node){
// summary:
// Scrolls the tab list to the left or right by 75% of the widget width.
// direction:
// If the direction is 1, the widget scrolls to the right, if it is
// -1, it scrolls to the left.
if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
var sWidth = dojo.style(this.scrollNode, "width");
var d = (sWidth * 0.75) * direction;
var to = this._getScroll() + d;
this._setButtonClass(to);
this.createSmoothScroll(to).play();
},
_setButtonClass: function(/*Number*/ scroll){
// summary:
// Disables the left scroll button if the tabs are scrolled all the way to the left,
// or the right scroll button in the opposite case.
// scroll: Integer
// amount of horizontal scroll
var scrollBounds = this._getScrollBounds();
this._leftBtn.set("disabled", scroll <= scrollBounds.min);
this._rightBtn.set("disabled", scroll >= scrollBounds.max);
}
});
dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
baseClass: "dijitTab tabStripButton",
templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "\n\t
\n\t\t
\n\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n
\n"),
// Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
// able to tab to the left/right/menu buttons
tabIndex: "",
// Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
// either (this override avoids focus() call in FormWidget.js)
isFocusable: function(){ return false; }
});
dojo.declare("dijit.layout._ScrollingTabControllerButton",
[dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]);
dojo.declare(
"dijit.layout._ScrollingTabControllerMenuButton",
[dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin],
{
// id of the TabContainer itself
containerId: "",
// -1 so user can't tab into the button, but so that button can still be focused programatically.
// Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
tabIndex: "-1",
isLoaded: function(){
// recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
return false;
},
loadDropDown: function(callback){
this.dropDown = new dijit.Menu({
id: this.containerId + "_menu",
dir: this.dir,
lang: this.lang
});
var container = dijit.byId(this.containerId);
dojo.forEach(container.getChildren(), function(page){
var menuItem = new dijit.MenuItem({
id: page.id + "_stcMi",
label: page.title,
iconClass: page.iconClass,
dir: page.dir,
lang: page.lang,
onClick: function(){
container.selectChild(page);
}
});
this.dropDown.addChild(menuItem);
}, this);
callback();
},
closeDropDown: function(/*Boolean*/ focus){
this.inherited(arguments);
if(this.dropDown){
this.dropDown.destroyRecursive();
delete this.dropDown;
}
}
});
}
if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.TabContainer"] = true;
dojo.provide("dijit.layout.TabContainer");
dojo.declare("dijit.layout.TabContainer",
dijit.layout._TabContainerBase,
{
// summary:
// A Container with tabs to select each child (only one of which is displayed at a time).
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// controllerWidget: String
// An optional parameter to override the widget used to display the tab labels
controllerWidget: "",
_makeController: function(/*DomNode*/ srcNode){
// summary:
// Instantiate tablist controller widget and return reference to it.
// Callback from _TabContainerBase.postCreate().
// tags:
// protected extension
var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
TabController = dojo.getObject(this.controllerWidget);
return new TabController({
id: this.id + "_tablist",
dir: this.dir,
lang: this.lang,
tabPosition: this.tabPosition,
doLayout: this.doLayout,
containerId: this.id,
"class": cls,
nested: this.nested,
useMenu: this.useMenu,
useSlider: this.useSlider,
tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
}, srcNode);
},
postMixInProperties: function(){
this.inherited(arguments);
// Scrolling controller only works for horizontal non-nested tabs
if(!this.controllerWidget){
this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
"dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
}
}
});
}
if(!dojo._hasResource["dijit.TitlePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.TitlePane"] = true;
dojo.provide("dijit.TitlePane");
dojo.declare(
"dijit.TitlePane",
[dijit.layout.ContentPane, dijit._Templated, dijit._CssStateMixin],
{
// summary:
// A pane with a title on top, that can be expanded or collapsed.
//
// description:
// An accessible container with a title Heading, and a content
// section that slides open and closed. TitlePane is an extension to
// `dijit.layout.ContentPane`, providing all the useful content-control aspects from it.
//
// example:
// | // load a TitlePane from remote file:
// | var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
// | foo.startup();
//
// example:
// |
// |
//
// example:
// |
// |
// title: String
// Title of the pane
title: "",
// open: Boolean
// Whether pane is opened or closed.
open: true,
// toggleable: Boolean
// Whether pane can be opened or closed by clicking the title bar.
toggleable: true,
// tabIndex: String
// Tabindex setting for the title (so users can tab to the title then
// use space/enter to open/close the title pane)
tabIndex: "0",
// duration: Integer
// Time in milliseconds to fade in/fade out
duration: dijit.defaultDuration,
// baseClass: [protected] String
// The root className to be placed on this widget's domNode.
baseClass: "dijitTitlePane",
templateString: dojo.cache("dijit", "templates/TitlePane.html", "\n\t
\n\t\t
\n\t\t\t
\n\t\t
\n\t
\n\t
\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t
\n
\n"),
attributeMap: dojo.delegate(dijit.layout.ContentPane.prototype.attributeMap, {
title: { node: "titleNode", type: "innerHTML" },
tooltip: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
id:""
}),
buildRendering: function(){
this.inherited(arguments);
dojo.setSelectable(this.titleNode, false);
},
postCreate: function(){
this.inherited(arguments);
// Hover and focus effect on title bar, except for non-toggleable TitlePanes
// This should really be controlled from _setToggleableAttr() but _CssStateMixin
// doesn't provide a way to disconnect a previous _trackMouseState() call
if(this.toggleable){
this._trackMouseState(this.titleBarNode, "dijitTitlePaneTitle");
}
// setup open/close animations
var hideNode = this.hideNode, wipeNode = this.wipeNode;
this._wipeIn = dojo.fx.wipeIn({
node: this.wipeNode,
duration: this.duration,
beforeBegin: function(){
hideNode.style.display="";
}
});
this._wipeOut = dojo.fx.wipeOut({
node: this.wipeNode,
duration: this.duration,
onEnd: function(){
hideNode.style.display="none";
}
});
},
_setOpenAttr: function(/*Boolean*/ open, /*Boolean*/ animate){
// summary:
// Hook to make set("open", boolean) control the open/closed state of the pane.
// open: Boolean
// True if you want to open the pane, false if you want to close it.
dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
if(animation && animation.status() == "playing"){
animation.stop();
}
});
if(animate){
var anim = this[open ? "_wipeIn" : "_wipeOut"];
anim.play();
}else{
this.hideNode.style.display = this.wipeNode.style.display = open ? "" : "none";
}
// load content (if this is the first time we are opening the TitlePane
// and content is specified as an href, or href was set when hidden)
if(this._started){
if(open){
this._onShow();
}else{
this.onHide();
}
}
this.arrowNodeInner.innerHTML = open ? "-" : "+";
dijit.setWaiState(this.containerNode,"hidden", open ? "false" : "true");
dijit.setWaiState(this.focusNode, "pressed", open ? "true" : "false");
this._set("open", open);
this._setCss();
},
_setToggleableAttr: function(/*Boolean*/ canToggle){
// summary:
// Hook to make set("toggleable", boolean) work.
// canToggle: Boolean
// True to allow user to open/close pane by clicking title bar.
dijit.setWaiRole(this.focusNode, canToggle ? "button" : "heading");
if(canToggle){
// TODO: if canToggle is switched from true to false shouldn't we remove this setting?
dijit.setWaiState(this.focusNode, "controls", this.id+"_pane");
dojo.attr(this.focusNode, "tabIndex", this.tabIndex);
}else{
dojo.removeAttr(this.focusNode, "tabIndex");
}
this._set("toggleable", canToggle);
this._setCss();
},
_setContentAttr: function(/*String|DomNode|Nodelist*/ content){
// summary:
// Hook to make set("content", ...) work.
// Typically called when an href is loaded. Our job is to make the animation smooth.
if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
// we are currently *closing* the pane (or the pane is closed), so just let that continue
this.inherited(arguments);
}else{
if(this._wipeIn && this._wipeIn.status() == "playing"){
this._wipeIn.stop();
}
// freeze container at current height so that adding new content doesn't make it jump
dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });
// add the new content (erasing the old content, if any)
this.inherited(arguments);
// call _wipeIn.play() to animate from current height to new height
if(this._wipeIn){
this._wipeIn.play();
}else{
this.hideNode.style.display = "";
}
}
},
toggle: function(){
// summary:
// Switches between opened and closed state
// tags:
// private
this._setOpenAttr(!this.open, true);
},
_setCss: function(){
// summary:
// Set the open/close css state for the TitlePane
// tags:
// private
var node = this.titleBarNode || this.focusNode;
var oldCls = this._titleBarClass;
this._titleBarClass = "dijit" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
dojo.replaceClass(node, this._titleBarClass, oldCls || "");
this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
},
_onTitleKey: function(/*Event*/ e){
// summary:
// Handler for when user hits a key
// tags:
// private
if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
if(this.toggleable){
this.toggle();
}
dojo.stopEvent(e);
}else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
this.containerNode.focus();
e.preventDefault();
}
},
_onTitleClick: function(){
// summary:
// Handler when user clicks the title bar
// tags:
// private
if(this.toggleable){
this.toggle();
}
},
setTitle: function(/*String*/ title){
// summary:
// Deprecated. Use set('title', ...) instead.
// tags:
// deprecated
dojo.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
this.set("title", title);
}
});
}
if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.DeferredList"] = true;
dojo.provide("dojo.DeferredList");
dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
// summary:
// Provides event handling for a group of Deferred objects.
// description:
// DeferredList takes an array of existing deferreds and returns a new deferred of its own
// this new deferred will typically have its callback fired when all of the deferreds in
// the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
// fireOnOneErrback, will fire before all the deferreds as appropriate
//
// list:
// The list of deferreds to be synchronizied with this DeferredList
// fireOnOneCallback:
// Will cause the DeferredLists callback to be fired as soon as any
// of the deferreds in its list have been fired instead of waiting until
// the entire list has finished
// fireonOneErrback:
// Will cause the errback to fire upon any of the deferreds errback
// canceller:
// A deferred canceller function, see dojo.Deferred
var resultList = [];
dojo.Deferred.call(this);
var self = this;
if(list.length === 0 && !fireOnOneCallback){
this.resolve([0, []]);
}
var finished = 0;
dojo.forEach(list, function(item, i){
item.then(function(result){
if(fireOnOneCallback){
self.resolve([i, result]);
}else{
addResult(true, result);
}
},function(error){
if(fireOnOneErrback){
self.reject(error);
}else{
addResult(false, error);
}
if(consumeErrors){
return null;
}
throw error;
});
function addResult(succeeded, result){
resultList[i] = [succeeded, result];
finished++;
if(finished === list.length){
self.resolve(resultList);
}
}
});
};
dojo.DeferredList.prototype = new dojo.Deferred();
dojo.DeferredList.prototype.gatherResults= function(deferredList){
// summary:
// Gathers the results of the deferreds for packaging
// as the parameters to the Deferred Lists' callback
var d = new dojo.DeferredList(deferredList, false, true, false);
d.addCallback(function(results){
var ret = [];
dojo.forEach(results, function(result){
ret.push(result[1]);
});
return ret;
});
return d;
};
}
if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
dojo.provide("dijit.tree.TreeStoreModel");
dojo.declare(
"dijit.tree.TreeStoreModel",
null,
{
// summary:
// Implements dijit.Tree.model connecting to a store with a single
// root item. Any methods passed into the constructor will override
// the ones defined here.
// store: dojo.data.Store
// Underlying store
store: null,
// childrenAttrs: String[]
// One or more attribute names (attributes in the dojo.data item) that specify that item's children
childrenAttrs: ["children"],
// newItemIdAttr: String
// Name of attribute in the Object passed to newItem() that specifies the id.
//
// If newItemIdAttr is set then it's used when newItem() is called to see if an
// item with the same id already exists, and if so just links to the old item
// (so that the old item ends up with two parents).
//
// Setting this to null or "" will make every drop create a new item.
newItemIdAttr: "id",
// labelAttr: String
// If specified, get label for tree node from this attribute, rather
// than by calling store.getLabel()
labelAttr: "",
// root: [readonly] dojo.data.Item
// Pointer to the root item (read only, not a parameter)
root: null,
// query: anything
// Specifies datastore query to return the root item for the tree.
// Must only return a single item. Alternately can just pass in pointer
// to root item.
// example:
// | {id:'ROOT'}
query: null,
// deferItemLoadingUntilExpand: Boolean
// Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
// until they are expanded. This allows for lazying loading where only one
// loadItem (and generally one network call, consequently) per expansion
// (rather than one for each child).
// This relies on partial loading of the children items; each children item of a
// fully loaded item should contain the label and info about having children.
deferItemLoadingUntilExpand: false,
constructor: function(/* Object */ args){
// summary:
// Passed the arguments listed above (store, etc)
// tags:
// private
dojo.mixin(this, args);
this.connects = [];
var store = this.store;
if(!store.getFeatures()['dojo.data.api.Identity']){
throw new Error("dijit.Tree: store must support dojo.data.Identity");
}
// if the store supports Notification, subscribe to the notification events
if(store.getFeatures()['dojo.data.api.Notification']){
this.connects = this.connects.concat([
dojo.connect(store, "onNew", this, "onNewItem"),
dojo.connect(store, "onDelete", this, "onDeleteItem"),
dojo.connect(store, "onSet", this, "onSetItem")
]);
}
},
destroy: function(){
dojo.forEach(this.connects, dojo.disconnect);
// TODO: should cancel any in-progress processing of getRoot(), getChildren()
},
// =======================================================================
// Methods for traversing hierarchy
getRoot: function(onItem, onError){
// summary:
// Calls onItem with the root item for the tree, possibly a fabricated item.
// Calls onError on error.
if(this.root){
onItem(this.root);
}else{
this.store.fetch({
query: this.query,
onComplete: dojo.hitch(this, function(items){
if(items.length != 1){
throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
" items, but must return exactly one item");
}
this.root = items[0];
onItem(this.root);
}),
onError: onError
});
}
},
mayHaveChildren: function(/*dojo.data.Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
return dojo.some(this.childrenAttrs, function(attr){
return this.store.hasAttribute(item, attr);
}, this);
},
getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
var store = this.store;
if(!store.isItemLoaded(parentItem)){
// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
// mode, so we will load it and just return the children (without loading each
// child item)
var getChildren = dojo.hitch(this, arguments.callee);
store.loadItem({
item: parentItem,
onItem: function(parentItem){
getChildren(parentItem, onComplete, onError);
},
onError: onError
});
return;
}
// get children of specified item
var childItems = [];
for(var i=0; i\n\t\t\t \n\t\t \n\t
\n\n"),
baseClass: "dijitTreeNode",
// For hover effect for tree node, and focus effect for label
cssStateNodes: {
rowNode: "dijitTreeRow",
labelNode: "dijitTreeLabel"
},
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
label: {node: "labelNode", type: "innerText"},
tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
}),
buildRendering: function(){
this.inherited(arguments);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
}
//aria-selected should be false on all selectable elements.
this.setSelected(false);
},
_setIndentAttr: function(indent){
// summary:
// Tell this node how many levels it should be indented
// description:
// 0 for top level nodes, 1 for their children, 2 for their
// grandchildren, etc.
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
dojo.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
this._set("indent", indent);
},
markProcessing: function(){
// summary:
// Visually denote that tree is loading data, etc.
// tags:
// private
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary:
// Clear markup from markProcessing() call
// tags:
// private
this._setExpando(false);
},
_updateItemClasses: function(item){
// summary:
// Set appropriate CSS classes for icon and label dom node
// (used to allow for item updates to change respective CSS)
// tags:
// private
var tree = this.tree, model = tree.model;
if(tree._v10Compat && item === model.root){
// For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
item = null;
}
this._applyClassAndStyle(item, "icon", "Icon");
this._applyClassAndStyle(item, "label", "Label");
this._applyClassAndStyle(item, "row", "Row");
},
_applyClassAndStyle: function(item, lower, upper){
// summary:
// Set the appropriate CSS classes and styles for labels, icons and rows.
//
// item:
// The data item.
//
// lower:
// The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
//
// upper:
// The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
//
// tags:
// private
var clsName = "_" + lower + "Class";
var nodeName = lower + "Node";
var oldCls = this[clsName];
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
// summary:
// Set appropriate CSS classes for this.domNode
// tags:
// private
var parent = this.getParent();
if(!parent || parent.rowNode.style.display == "none"){
/* if we are hiding the root node then make every first level child look like a root node */
dojo.addClass(this.domNode, "dijitTreeIsRoot");
}else{
dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary:
// Set the right image for the expando node
// tags:
// private
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
_a11yStates = ["*","-","+","*"],
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
dojo.replaceClass(this.expandoNode, styles[idx], styles);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
},
expand: function(){
// summary:
// Show my children
// returns:
// Deferred that fires when expansion is complete
// If there's already an expand in progress or we are already expanded, just return
if(this._expandDeferred){
return this._expandDeferred; // dojo.Deferred
}
// cancel in progress collapse operation
this._wipeOut && this._wipeOut.stop();
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
dijit.setWaiState(this.labelNode, "expanded", "true");
if(this.tree.showRoot || this !== this.tree.rootNode){
dijit.setWaiRole(this.containerNode, "group");
}
dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode){
dijit.setWaiState(this.tree.domNode, "expanded", "true");
}
var def,
wipeIn = dojo.fx.wipeIn({
node: this.containerNode, duration: dijit.defaultDuration,
onEnd: function(){
def.callback(true);
}
});
// Deferred that fires when expand is complete
def = (this._expandDeferred = new dojo.Deferred(function(){
// Canceller
wipeIn.stop();
}));
wipeIn.play();
return def; // dojo.Deferred
},
collapse: function(){
// summary:
// Collapse this node (if it's expanded)
if(!this.isExpanded){ return; }
// cancel in progress expand operation
if(this._expandDeferred){
this._expandDeferred.cancel();
delete this._expandDeferred;
}
this.isExpanded = false;
dijit.setWaiState(this.labelNode, "expanded", "false");
if(this == this.tree.rootNode){
dijit.setWaiState(this.tree.domNode, "expanded", "false");
}
dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(!this._wipeOut){
this._wipeOut = dojo.fx.wipeOut({
node: this.containerNode, duration: dijit.defaultDuration
});
}
this._wipeOut.play();
},
// indent: Integer
// Levels from this node to the root node
indent: 0,
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
// Also, if this.persist == true, expands any children that were previously
// opened.
// returns:
// Deferred object that fires after all previously opened children
// have been expanded again (or fires instantly if there are no such children).
var tree = this.tree,
model = tree.model,
defs = []; // list of deferreds that need to fire before I am complete
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
dojo.forEach(this.getChildren(), function(child){
dijit._Container.prototype.removeChild.call(this, child);
}, this);
this.state = "LOADED";
if(items && items.length > 0){
this.isExpandable = true;
// Create _TreeNode widget for each specified tree node, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
dojo.forEach(items, function(item){
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
if(existingNodes){
for(var i=0;i itself
tree.domNode.setAttribute("tabIndex", "0");
}
}
return new dojo.DeferredList(defs); // dojo.Deferred
},
getTreePath: function(){
var node = this;
var path = [];
while(node && node !== this.tree.rootNode){
path.unshift(node.item);
node = node.getParent();
}
path.unshift(this.tree.rootNode.item);
return path;
},
getIdentity: function() {
return this.tree.model.getIdentity(this.item);
},
removeChild: function(/* treeNode */ node){
this.inherited(arguments);
var children = this.getChildren();
if(children.length == 0){
this.isExpandable = false;
this.collapse();
}
dojo.forEach(children, function(child){
child._updateLayout();
});
},
makeExpandable: function(){
// summary:
// if this node wasn't already showing the expando node,
// turn it into one and call _setExpando()
// TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
this.isExpandable = true;
this._setExpando(false);
},
_onLabelFocus: function(evt){
// summary:
// Called when this row is focused (possibly programatically)
// Note that we aren't using _onFocus() builtin to dijit
// because it's called when focus is moved to a descendant TreeNode.
// tags:
// private
this.tree._onNodeFocus(this);
},
setSelected: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) currently selected node.
// Mark that this node is/isn't that currently selected node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
dijit.setWaiState(this.labelNode, "selected", selected);
dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
},
setFocusable: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) node that's focusable.
// Mark that this node is/isn't that currently focsuable node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
},
_onClick: function(evt){
// summary:
// Handler for onclick event on a node
// tags:
// private
this.tree._onClick(this, evt);
},
_onDblClick: function(evt){
// summary:
// Handler for ondblclick event on a node
// tags:
// private
this.tree._onDblClick(this, evt);
},
_onMouseEnter: function(evt){
// summary:
// Handler for onmouseenter event on a node
// tags:
// private
this.tree._onNodeMouseEnter(this, evt);
},
_onMouseLeave: function(evt){
// summary:
// Handler for onmouseenter event on a node
// tags:
// private
this.tree._onNodeMouseLeave(this, evt);
}
});
dojo.declare(
"dijit.Tree",
[dijit._Widget, dijit._Templated],
{
// summary:
// This widget displays hierarchical data from a store.
// store: [deprecated] String||dojo.data.Store
// Deprecated. Use "model" parameter instead.
// The store to get data to display in the tree.
store: null,
// model: dijit.Tree.model
// Interface to read tree data, get notifications of changes to tree data,
// and for handling drop operations (i.e drag and drop onto the tree)
model: null,
// query: [deprecated] anything
// Deprecated. User should specify query to the model directly instead.
// Specifies datastore query to return the root item or top items for the tree.
query: null,
// label: [deprecated] String
// Deprecated. Use dijit.tree.ForestStoreModel directly instead.
// Used in conjunction with query parameter.
// If a query is specified (rather than a root node id), and a label is also specified,
// then a fake root node is created and displayed, with this label.
label: "",
// showRoot: [const] Boolean
// Should the root node be displayed, or hidden?
showRoot: true,
// childrenAttr: [deprecated] String[]
// Deprecated. This information should be specified in the model.
// One ore more attributes that holds children of a tree node
childrenAttr: ["children"],
// paths: String[][] or Item[][]
// Full paths from rootNode to selected nodes expressed as array of items or array of ids.
// Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
// returns a Deferred to indicate when the set is complete.
paths: [],
// path: String[] or Item[]
// Backward compatible singular variant of paths.
path: [],
// selectedItems: [readonly] Item[]
// The currently selected items in this tree.
// This property can only be set (via set('selectedItems', ...)) when that item is already
// visible in the tree. (I.e. the tree has already been expanded to show that node.)
// Should generally use `paths` attribute to set the selected items instead.
selectedItems: null,
// selectedItem: [readonly] Item
// Backward compatible singular variant of selectedItems.
selectedItem: null,
// openOnClick: Boolean
// If true, clicking a folder node's label will open it, rather than calling onClick()
openOnClick: false,
// openOnDblClick: Boolean
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
templateString: dojo.cache("dijit", "templates/Tree.html", "\n"),
// persist: Boolean
// Enables/disables use of cookies for state saving.
persist: true,
// autoExpand: Boolean
// Fully expand the tree on load. Overrides `persist`.
autoExpand: false,
// dndController: [protected] String
// Class name to use as as the dnd controller. Specifying this class enables DnD.
// Generally you should specify this as "dijit.tree.dndSource".
// Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
dndController: "dijit.tree._dndSelector",
// parameters to pull off of the tree and pass on to the dndController as its params
dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
//declare the above items so they can be pulled from the tree's markup
// onDndDrop: [protected] Function
// Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
// Generally this doesn't need to be set.
onDndDrop: null,
/*=====
itemCreator: function(nodes, target, source){
// summary:
// Returns objects passed to `Tree.model.newItem()` based on DnD nodes
// dropped onto the tree. Developer must override this method to enable
// dropping from external sources onto this Tree, unless the Tree.model's items
// happen to look like {id: 123, name: "Apple" } with no other attributes.
// description:
// For each node in nodes[], which came from source, create a hash of name/value
// pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
// nodes: DomNode[]
// The DOMNodes dragged from the source container
// target: DomNode
// The target TreeNode.rowNode
// source: dojo.dnd.Source
// The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
// returns: Object[]
// Array of name/value hashes for each new item to be added to the Tree, like:
// | [
// | { id: 123, label: "apple", foo: "bar" },
// | { id: 456, label: "pear", zaz: "bam" }
// | ]
// tags:
// extension
return [{}];
},
=====*/
itemCreator: null,
// onDndCancel: [protected] Function
// Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
// Generally this doesn't need to be set.
onDndCancel: null,
/*=====
checkAcceptance: function(source, nodes){
// summary:
// Checks if the Tree itself can accept nodes from this source
// source: dijit.tree._dndSource
// The source which provides items
// nodes: DOMNode[]
// Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
// source is a dijit.Tree.
// tags:
// extension
return true; // Boolean
},
=====*/
checkAcceptance: null,
/*=====
checkItemAcceptance: function(target, source, position){
// summary:
// Stub function to be overridden if one wants to check for the ability to drop at the node/item level
// description:
// In the base case, this is called to check if target can become a child of source.
// When betweenThreshold is set, position="before" or "after" means that we
// are asking if the source node can be dropped before/after the target node.
// target: DOMNode
// The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
// Use dijit.getEnclosingWidget(target) to get the TreeNode.
// source: dijit.tree.dndSource
// The (set of) nodes we are dropping
// position: String
// "over", "before", or "after"
// tags:
// extension
return true; // Boolean
},
=====*/
checkItemAcceptance: null,
// dragThreshold: Integer
// Number of pixels mouse moves before it's considered the start of a drag operation
dragThreshold: 5,
// betweenThreshold: Integer
// Set to a positive value to allow drag and drop "between" nodes.
//
// If during DnD mouse is over a (target) node but less than betweenThreshold
// pixels from the bottom edge, dropping the the dragged node will make it
// the next sibling of the target node, rather than the child.
//
// Similarly, if mouse is over a target node but less that betweenThreshold
// pixels from the top edge, dropping the dragged node will make it
// the target node's previous sibling rather than the target node's child.
betweenThreshold: 0,
// _nodePixelIndent: Integer
// Number of pixels to indent tree nodes (relative to parent node).
// Default is 19 but can be overridden by setting CSS class dijitTreeIndent
// and calling resize() or startup() on tree after it's in the DOM.
_nodePixelIndent: 19,
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
},
postMixInProperties: function(){
this.tree = this;
if(this.autoExpand){
// There's little point in saving opened/closed state of nodes for a Tree
// that initially opens all it's nodes.
this.persist = false;
}
this._itemNodesMap={};
if(!this.cookieName){
this.cookieName = this.id + "SaveStateCookie";
}
this._loadDeferred = new dojo.Deferred();
this.inherited(arguments);
},
postCreate: function(){
this._initState();
// Create glue between store and Tree, if not specified directly by user
if(!this.model){
this._store2model();
}
// monitor changes to items
this.connect(this.model, "onChange", "_onItemChange");
this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
this.connect(this.model, "onDelete", "_onItemDelete");
this._load();
this.inherited(arguments);
if(this.dndController){
if(dojo.isString(this.dndController)){
this.dndController = dojo.getObject(this.dndController);
}
var params={};
for(var i=0; i1){
var _document = node.ownerDocument || dojo.doc; //Preference is to get the node owning doc first or it may fail
dojox.xml.parser.replaceChildren(node, _document.createTextNode(text));
return text; // String
}else{
if(node.textContent !== undefined){ //FF 1.5 -- remove?
return node.textContent; // String
}
var _result = "";
if(node){
dojo.forEach(node.childNodes, function(child){
switch(child.nodeType){
case 1: // ELEMENT_NODE
case 5: // ENTITY_REFERENCE_NODE
_result += dojox.xml.parser.textContent(child);
break;
case 3: // TEXT_NODE
case 2: // ATTRIBUTE_NODE
case 4: // CDATA_SECTION_NODE
_result += child.nodeValue;
}
});
}
return _result; // String
}
}
dojox.xml.parser.replaceChildren = function(/*Element*/node, /*Node || Array*/ newChildren){
// summary:
// Removes all children of node and appends newChild. All the existing
// children will be destroyed.
// description:
// Removes all children of node and appends newChild. All the existing
// children will be destroyed.
// node:
// The node to modify the children on
// newChildren:
// The children to add to the node. It can either be a single Node or an
// array of Nodes.
var nodes = [];
if(dojo.isIE){
dojo.forEach(node.childNodes, function(child){
nodes.push(child);
});
}
dojox.xml.parser.removeChildren(node);
dojo.forEach(nodes, dojo.destroy);
if(!dojo.isArray(newChildren)){
node.appendChild(newChildren);
}else{
dojo.forEach(newChildren, function(child){
node.appendChild(child);
});
}
}
dojox.xml.parser.removeChildren = function(/*Element*/node){
// summary:
// removes all children from node and returns the count of children removed.
// The children nodes are not destroyed. Be sure to call dojo.destroy on them
// after they are not used anymore.
// node:
// The node to remove all the children from.
var count = node.childNodes.length;
while(node.hasChildNodes()){
node.removeChild(node.firstChild);
}
return count; // int
}
dojox.xml.parser.innerXML = function(/*Node*/node){
// summary:
// Implementation of MS's innerXML function.
// node:
// The node from which to generate the XML text representation.
if(node.innerXML){
return node.innerXML; // String
}else if(node.xml){
return node.xml; // String
}else if(typeof XMLSerializer != "undefined"){
return (new XMLSerializer()).serializeToString(node); // String
}
return null;
}
}
if(!dojo._hasResource["dojox.xml.DomParser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.xml.DomParser"] = true;
dojo.provide("dojox.xml.DomParser");
dojox.xml.DomParser=new (function(){
/**********************************************************
* The DomParser is a close-to (but not entirely)
* conforming XML parser based on regular
* expressions. It will take any XML fragment
* and return a lightweight JS structure that is
* similar to (but not exactly) the DOM specification.
*
* Getter and setter methods are NOT available; the goal
* was to keep the resulting object model entirely JS-like.
*
* All node types but document fragments are supported;
* all nodes support getElementsByTagName and
* getElementsByTagNameNS (with short names byName and
* byNameNS). The document node supports getElementById
* (byId), and all nodes support a supplimental
* childrenByName/childrenByNameNS method as well.
*
* The object model is intended to be a READONLY format;
* mutation events are NOT supported, and though you
* can change properties on a node-by-node basis, certain
* operations are not supported (such as changing the ID
* of an element).
**********************************************************/
// internal use only.
var nodeTypes={ ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9 };
// compile the regular expressions once.
var reTags=/<([^>\/\s+]*)([^>]*)>([^<]*)/g;
var reAttr=/([^=]*)=(("([^"]*)")|('([^']*)'))/g; // patch from tdedischew AT gmail, with additional grouping
var reEntity=//g;
var reCData=//g;
var reComments=//g;
var trim=/^\s+|\s+$/g;
var normalize=/\s+/g;
var egt=/\>/g;
var elt=/\</g;
var equot=/\"/g;
var eapos=/\'/g;
var eamp=/\&/g;
var dNs="_def_";
// create a root node.
function _doc(){
return new (function(){
var all={};
this.nodeType=nodeTypes.DOCUMENT;
this.nodeName="#document";
this.namespaces={};
this._nsPaths={};
this.childNodes=[];
this.documentElement=null;
// any element with an ID attribute will be added to the internal hashtable.
this._add=function(obj){
if(typeof(obj.id)!="undefined"){ all[obj.id]=obj; }
};
this._remove=function(id){
if(all[id]){ delete all[id]; }
};
this.byId=this.getElementById=function(id){ return all[id]; };
this.byName=this.getElementsByTagName=byName;
this.byNameNS=this.getElementsByTagNameNS=byNameNS;
this.childrenByName=childrenByName;
this.childrenByNameNS=childrenByNameNS;
})();
}
// functions attached to element nodes
function byName(name){
// return all descendants with name. Fully qualified (i.e. svg:svg)
function __(node, name, arr){
dojo.forEach(node.childNodes, function(c){
if(c.nodeType==nodeTypes.ELEMENT){
if(name=="*"){ arr.push(c); }
else if(c.nodeName==name){ arr.push(c); }
__(c, name, arr);
}
});
}
var a=[];
__(this, name, a);
return a;
}
function byNameNS(name, ns){
// return all descendants with name by namespace. If no namespace passed, the default is used.
function __(node, name, ns, arr){
dojo.forEach(node.childNodes, function(c){
if(c.nodeType==nodeTypes.ELEMENT){
if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
__(c, name, ns, arr);
}
});
}
if(!ns){ ns=dNs; }
var a=[];
__(this, name, ns, a);
return a;
}
// Only child nodes with name.
function childrenByName(name){
var a=[];
dojo.forEach(this.childNodes, function(c){
if(c.nodeType==nodeTypes.ELEMENT){
if(name=="*"){ a.push(c); }
else if(c.nodeName==name){ a.push(c); }
}
});
return a;
}
function childrenByNameNS(name, ns){
var a=[];
dojo.forEach(this.childNodes, function(c){
if(c.nodeType==nodeTypes.ELEMENT){
if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
}
});
return a;
}
function _createTextNode(v){
return {
nodeType:nodeTypes.TEXT,
nodeName:"#text",
nodeValue:v.replace(normalize," ").replace(egt,">").replace(elt,"<").replace(eapos,"'").replace(equot,'"').replace(eamp,"&")
};
}
// attribute functions
function getAttr(name){
for(var i=0; i0){
return p.childNodes[i-1];
}
}
}
return null;
}
function next(){
var p=this.parentNode;
if(p){
for(var i=0;i0){
var entity, eRe=[];
if(reEntity.test(str)){
reEntity.lastIndex=0;
// match entities
while((entity=reEntity.exec(str))!=null){
eRe.push({
entity:"&"+entity[1].replace(trim,"")+";",
expression:entity[2]
});
}
// replace instances in the document.
for(var i=0; i1){
if(obj.parentNode){
obj=obj.parentNode;
}
var text=(res[3]||"").replace(trim, "");
if(text.length>0) {
obj.childNodes.push(_createTextNode(text));
}
}
// open tags.
else if(res[1].length>0){
// figure out the type of node.
if(res[1].charAt(0)=="?"){
// processing instruction
var name=res[1].substr(1);
var target=res[2].substr(0,res[2].length-2);
obj.childNodes.push({
nodeType:nodeTypes.PROCESSING_INSTRUCTION,
nodeName:name,
nodeValue:target
});
}
else if(res[1].charAt(0)=="!"){
// CDATA; skip over any declaration elements.
if(res[1].indexOf("![CDATA[")==0){
var val=parseInt(res[1].replace("![CDATA[","").replace("]]",""));
obj.childNodes.push({
nodeType:nodeTypes.CDATA_SECTION,
nodeName:"#cdata-section",
nodeValue:cdSections[val]
});
}
// Comments.
else if(res[1].substr(0,3)=="!--"){
var val=parseInt(res[1].replace("!--","").replace("--",""));
obj.childNodes.push({
nodeType:nodeTypes.COMMENT,
nodeName:"#comment",
nodeValue:comments[val]
});
}
}
else {
// Elements (with attribute and text)
var name=res[1].replace(trim,"");
var o={
nodeType:nodeTypes.ELEMENT,
nodeName:name,
localName:name,
namespace:dNs,
ownerDocument:root,
attributes:[],
parentNode:null,
childNodes:[]
};
// check to see if it's namespaced.
if(name.indexOf(":")>-1){
var t=name.split(":");
o.namespace=t[0];
o.localName=t[1];
}
// set the function references.
o.byName=o.getElementsByTagName=byName;
o.byNameNS=o.getElementsByTagNameNS=byNameNS;
o.childrenByName=childrenByName;
o.childrenByNameNS=childrenByNameNS;
o.getAttribute=getAttr;
o.getAttributeNS=getAttrNS;
o.setAttribute=setAttr;
o.setAttributeNS=setAttrNS;
o.previous=o.previousSibling=prev;
o.next=o.nextSibling=next;
// parse the attribute string.
var attr;
while((attr=reAttr.exec(res[2]))!=null){
if(attr.length>0){
var name=attr[1].replace(trim,"");
var val=(attr[4]||attr[6]||"").replace(normalize," ")
.replace(egt,">")
.replace(elt,"<")
.replace(eapos,"'")
.replace(equot,'"')
.replace(eamp,"&");
if(name.indexOf("xmlns")==0){
if(name.indexOf(":")>0){
var ns=name.split(":");
root.namespaces[ns[1]]=val;
root._nsPaths[val]=ns[1];
} else {
root.namespaces[dNs]=val;
root._nsPaths[val]=dNs;
}
} else {
var ln=name;
var ns=dNs;
if(name.indexOf(":")>0){
var t=name.split(":");
ln=t[1];
ns=t[0];
}
o.attributes.push({
nodeType:nodeTypes.ATTRIBUTE,
nodeName:name,
localName:ln,
namespace:ns,
nodeValue:val
});
// only add id as a property.
if(ln=="id"){ o.id=val; }
}
}
}
root._add(o);
if(obj){
obj.childNodes.push(o);
o.parentNode=obj;
// if it's not a self-closing node.
if(res[2].charAt(res[2].length-1)!="/"){
obj=o;
}
}
var text=res[3];
if(text.length>0){
obj.childNodes.push(_createTextNode(text));
}
}
}
}
// set the document element
for(var i=0; i=a.length); // bool
};
this.get=function(){
// summary
// Get the next member in the collection.
if(this.atEnd()){
return null; // object
}
this.element=a[position++];
return this.element; // object
};
this.map=function(/* function */fn, /* object? */scope){
// summary
// Functional iteration with optional scope.
return dojo.map(a, fn, scope);
};
this.reset=function(){
// summary
// reset the internal cursor.
position=0;
this.element=a[position];
};
}
/* Notes:
* The DictionaryIterator no longer supports a key and value property;
* the reality is that you can use this to iterate over a JS object
* being used as a hashtable.
*/
dojox.collections.DictionaryIterator=function(/* object */obj){
// summary
// return an object of type dojox.collections.DictionaryIterator
var a=[]; // Create an indexing array
var testObject={};
for(var p in obj){
if(!testObject[p]){
a.push(obj[p]); // fill it up
}
}
var position=0;
this.element=a[position]||null;
this.atEnd=function(){
// summary
// Test to see if the internal cursor has reached the end of the internal collection.
return (position>=a.length); // bool
};
this.get=function(){
// summary
// Get the next member in the collection.
if(this.atEnd()){
return null; // object
}
this.element=a[position++];
return this.element; // object
};
this.map=function(/* function */fn, /* object? */scope){
// summary
// Functional iteration with optional scope.
return dojo.map(a, fn, scope);
};
this.reset=function() {
// summary
// reset the internal cursor.
position=0;
this.element=a[position];
};
};
}
if(!dojo._hasResource["dojox.collections.Dictionary"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.collections.Dictionary"] = true;
dojo.provide("dojox.collections.Dictionary");
dojox.collections.Dictionary=function(/* dojox.collections.Dictionary? */dictionary){
// summary
// Returns an object of type dojox.collections.Dictionary
var items={};
this.count=0;
// comparator for property addition and access.
var testObject={};
this.add=function(/* string */k, /* object */v){
// summary
// Add a new item to the Dictionary.
var b=(k in items);
items[k]=new dojox.collections.DictionaryEntry(k,v);
if(!b){
this.count++;
}
};
this.clear=function(){
// summary
// Clears the internal dictionary.
items={};
this.count=0;
};
this.clone=function(){
// summary
// Returns a new instance of dojox.collections.Dictionary; note the the dictionary is a clone but items might not be.
return new dojox.collections.Dictionary(this); // dojox.collections.Dictionary
};
this.contains=this.containsKey=function(/* string */k){
// summary
// Check to see if the dictionary has an entry at key "k".
if(testObject[k]){
return false; // bool
}
return (items[k]!=null); // bool
};
this.containsValue=function(/* object */v){
// summary
// Check to see if the dictionary has an entry with value "v".
var e=this.getIterator();
while(e.get()){
if(e.element.value==v){
return true; // bool
}
}
return false; // bool
};
this.entry=function(/* string */k){
// summary
// Accessor method; similar to dojox.collections.Dictionary.item but returns the actual Entry object.
return items[k]; // dojox.collections.DictionaryEntry
};
this.forEach=function(/* function */ fn, /* object? */ scope){
// summary
// functional iterator, following the mozilla spec.
var a=[]; // Create an indexing array
for(var p in items) {
if(!testObject[p]){
a.push(items[p]); // fill it up
}
}
dojo.forEach(a, fn, scope);
};
this.getKeyList=function(){
// summary
// Returns an array of the keys in the dictionary.
return (this.getIterator()).map(function(entry){
return entry.key;
}); // array
};
this.getValueList=function(){
// summary
// Returns an array of the values in the dictionary.
return (this.getIterator()).map(function(entry){
return entry.value;
}); // array
};
this.item=function(/* string */k){
// summary
// Accessor method.
if(k in items){
return items[k].valueOf(); // object
}
return undefined; // object
};
this.getIterator=function(){
// summary
// Gets a dojox.collections.DictionaryIterator for iteration purposes.
return new dojox.collections.DictionaryIterator(items); // dojox.collections.DictionaryIterator
};
this.remove=function(/* string */k){
// summary
// Removes the item at k from the internal collection.
if(k in items && !testObject[k]){
delete items[k];
this.count--;
return true; // bool
}
return false; // bool
};
if (dictionary){
var e=dictionary.getIterator();
while(e.get()) {
this.add(e.element.key, e.element.value);
}
}
};
}
if(!dojo._hasResource["dojox.data.QueryReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.data.QueryReadStore"] = true;
dojo.provide("dojox.data.QueryReadStore");
dojo.declare("dojox.data.QueryReadStore",
null,
{
// summary:
// This class provides a store that is mainly intended to be used
// for loading data dynamically from the server, used i.e. for
// retreiving chunks of data from huge data stores on the server (by server-side filtering!).
// Upon calling the fetch() method of this store the data are requested from
// the server if they are not yet loaded for paging (or cached).
//
// For example used for a combobox which works on lots of data. It
// can be used to retreive the data partially upon entering the
// letters "ac" it returns only items like "action", "acting", etc.
//
// note:
// The field name "id" in a query is reserved for looking up data
// by id. This is necessary as before the first fetch, the store
// has no way of knowing which field the server will declare as
// identifier.
//
// example:
// | // The parameter "query" contains the data that are sent to the server.
// | var store = new dojox.data.QueryReadStore({url:'/search.php'});
// | store.fetch({query:{name:'a'}, queryOptions:{ignoreCase:false}});
//
// | // Since "serverQuery" is given, it overrules and those data are
// | // sent to the server.
// | var store = new dojox.data.QueryReadStore({url:'/search.php'});
// | store.fetch({serverQuery:{name:'a'}, queryOptions:{ignoreCase:false}});
//
// |
// |
// |
//
// todo:
// - there is a bug in the paging, when i set start:2, count:5 after an initial fetch() and doClientPaging:true
// it returns 6 elemetns, though count=5, try it in QueryReadStore.html
// - add optional caching
// - when the first query searched for "a" and the next for a subset of
// the first, i.e. "ab" then we actually dont need a server request, if
// we have client paging, we just need to filter the items we already have
// that might also be tooo much logic
url:"",
requestMethod:"get",
//useCache:false,
// We use the name in the errors, once the name is fixed hardcode it, may be.
_className:"dojox.data.QueryReadStore",
// This will contain the items we have loaded from the server.
// The contents of this array is optimized to satisfy all read-api requirements
// and for using lesser storage, so the keys and their content need some explaination:
// this._items[0].i - the item itself
// this._items[0].r - a reference to the store, so we can identify the item
// securly. We set this reference right after receiving the item from the
// server.
_items:[],
// Store the last query that triggered xhr request to the server.
// So we can compare if the request changed and if we shall reload
// (this also depends on other factors, such as is caching used, etc).
_lastServerQuery:null,
// Store how many rows we have so that we can pass it to a clientPaging handler
_numRows:-1,
// Store a hash of the last server request. Actually I introduced this
// for testing, so I can check if no unnecessary requests were issued for
// client-side-paging.
lastRequestHash:null,
// summary:
// By default every request for paging is sent to the server.
doClientPaging:false,
// summary:
// By default all the sorting is done serverside before the data is returned
// which is the proper place to be doing it for really large datasets.
doClientSorting:false,
// Items by identify for Identify API
_itemsByIdentity:null,
// Identifier used
_identifier:null,
_features: {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true},
_labelAttr: "label",
constructor: function(/* Object */ params){
dojo.mixin(this,params);
},
getValue: function(/* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){
// According to the Read API comments in getValue() and exception is
// thrown when an item is not an item or the attribute not a string!
this._assertIsItem(item);
if(!dojo.isString(attribute)){
throw new Error(this._className+".getValue(): Invalid attribute, string expected!");
}
if(!this.hasAttribute(item, attribute)){
// read api says: return defaultValue "only if *item* does not have a value for *attribute*."
// Is this the case here? The attribute doesn't exist, but a defaultValue, sounds reasonable.
if(defaultValue){
return defaultValue;
}
}
return item.i[attribute];
},
getValues: function(/* item */ item, /* attribute-name-string */ attribute){
this._assertIsItem(item);
var ret = [];
if(this.hasAttribute(item, attribute)){
ret.push(item.i[attribute]);
}
return ret;
},
getAttributes: function(/* item */ item){
this._assertIsItem(item);
var ret = [];
for(var i in item.i){
ret.push(i);
}
return ret;
},
hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
// summary:
// See dojo.data.api.Read.hasAttribute()
return this.isItem(item) && typeof item.i[attribute]!="undefined";
},
containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value){
var values = this.getValues(item, attribute);
var len = values.length;
for(var i=0; i>> var store = new dojox.data.QueryReadStore({});
// >>> store.isItem("");
// false
//
// >>> var store = new dojox.data.QueryReadStore({});
// >>> store.isItem({});
// false
//
// >>> var store = new dojox.data.QueryReadStore({});
// >>> store.isItem(0);
// false
//
// >>> var store = new dojox.data.QueryReadStore({});
// >>> store.isItem({name:"me", label:"me too"});
// false
//
if(something){
return typeof something.r != "undefined" && something.r == this;
}
return false;
},
isItemLoaded: function(/* anything */ something){
// Currently we dont have any state that tells if an item is loaded or not
// if the item exists its also loaded.
// This might change when we start working with refs inside items ...
return this.isItem(something);
},
loadItem: function(/* object */ args){
if(this.isItemLoaded(args.item)){
return;
}
// Actually we have nothing to do here, or at least I dont know what to do here ...
},
fetch:function(/* Object? */ request){
// summary:
// See dojo.data.util.simpleFetch.fetch() this is just a copy and I adjusted
// only the paging, since it happens on the server if doClientPaging is
// false, thx to http://trac.dojotoolkit.org/ticket/4761 reporting this.
// Would be nice to be able to use simpleFetch() to reduce copied code,
// but i dont know how yet. Ideas please!
request = request || {};
if(!request.store){
request.store = this;
}
var self = this;
var _errorHandler = function(errorData, requestObject){
if(requestObject.onError){
var scope = requestObject.scope || dojo.global;
requestObject.onError.call(scope, errorData, requestObject);
}
};
var _fetchHandler = function(items, requestObject, numRows){
var oldAbortFunction = requestObject.abort || null;
var aborted = false;
var startIndex = requestObject.start?requestObject.start:0;
if(self.doClientPaging == false){
// For client paging we dont need no slicing of the result.
startIndex = 0;
}
var endIndex = requestObject.count?(startIndex + requestObject.count):items.length;
requestObject.abort = function(){
aborted = true;
if(oldAbortFunction){
oldAbortFunction.call(requestObject);
}
};
var scope = requestObject.scope || dojo.global;
if(!requestObject.store){
requestObject.store = self;
}
if(requestObject.onBegin){
requestObject.onBegin.call(scope, numRows, requestObject);
}
if(requestObject.sort && self.doClientSorting){
items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
}
if(requestObject.onItem){
for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
var item = items[i];
if(!aborted){
requestObject.onItem.call(scope, item, requestObject);
}
}
}
if(requestObject.onComplete && !aborted){
var subset = null;
if(!requestObject.onItem){
subset = items.slice(startIndex, endIndex);
}
requestObject.onComplete.call(scope, subset, requestObject);
}
};
this._fetchItems(request, _fetchHandler, _errorHandler);
return request; // Object
},
getFeatures: function(){
return this._features;
},
close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
// I have no idea if this is really needed ...
},
getLabel: function(/* item */ item){
// summary:
// See dojo.data.api.Read.getLabel()
if(this._labelAttr && this.isItem(item)){
return this.getValue(item, this._labelAttr); //String
}
return undefined; //undefined
},
getLabelAttributes: function(/* item */ item){
// summary:
// See dojo.data.api.Read.getLabelAttributes()
if(this._labelAttr){
return [this._labelAttr]; //array
}
return null; //null
},
_xhrFetchHandler: function(data, request, fetchHandler, errorHandler){
data = this._filterResponse(data);
if(data.label){
this._labelAttr = data.label;
}
var numRows = data.numRows || -1;
this._items = [];
// Store a ref to "this" in each item, so we can simply check if an item
// really origins form here (idea is from ItemFileReadStore, I just don't know
// how efficient the real storage use, garbage collection effort, etc. is).
dojo.forEach(data.items,function(e){
this._items.push({i:e, r:this});
},this);
var identifier = data.identifier;
this._itemsByIdentity = {};
if(identifier){
this._identifier = identifier;
var i;
for(i = 0; i < this._items.length; ++i){
var item = this._items[i].i;
var identity = item[identifier];
if(!this._itemsByIdentity[identity]){
this._itemsByIdentity[identity] = item;
}else{
throw new Error(this._className+": The json data as specified by: [" + this.url + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
}
}
}else{
this._identifier = Number;
for(i = 0; i < this._items.length; ++i){
this._items[i].n = i;
}
}
// TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize
// (does it really sanititze them) and store the data optimal. should we? for security reasons???
numRows = this._numRows = (numRows === -1) ? this._items.length : numRows;
fetchHandler(this._items, request, numRows);
this._numRows = numRows;
},
_fetchItems: function(request, fetchHandler, errorHandler){
// summary:
// The request contains the data as defined in the Read-API.
// Additionally there is following keyword "serverQuery".
//
// The *serverQuery* parameter, optional.
// This parameter contains the data that will be sent to the server.
// If this parameter is not given the parameter "query"'s
// data are sent to the server. This is done for some reasons:
// - to specify explicitly which data are sent to the server, they
// might also be a mix of what is contained in "query", "queryOptions"
// and the paging parameters "start" and "count" or may be even
// completely different things.
// - don't modify the request.query data, so the interface using this
// store can rely on unmodified data, as the combobox dijit currently
// does it, it compares if the query has changed
// - request.query is required by the Read-API
//
// I.e. the following examples might be sent via GET:
// fetch({query:{name:"abc"}, queryOptions:{ignoreCase:true}})
// the URL will become: /url.php?name=abc
//
// fetch({serverQuery:{q:"abc", c:true}, query:{name:"abc"}, queryOptions:{ignoreCase:true}})
// the URL will become: /url.php?q=abc&c=true
// // The serverQuery-parameter has overruled the query-parameter
// // but the query parameter stays untouched, but is not sent to the server!
// // The serverQuery contains more data than the query, so they might differ!
//
var serverQuery = request.serverQuery || request.query || {};
//Need to add start and count
if(!this.doClientPaging){
serverQuery.start = request.start || 0;
// Count might not be sent if not given.
if(request.count){
serverQuery.count = request.count;
}
}
if(!this.doClientSorting && request.sort){
var sortInfo = [];
dojo.forEach(request.sort, function(sort){
if(sort && sort.attribute){
sortInfo.push((sort.descending ? "-" : "") + sort.attribute);
}
});
serverQuery.sort = sortInfo.join(',');
}
// Compare the last query and the current query by simply json-encoding them,
// so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()???
if(this.doClientPaging && this._lastServerQuery !== null &&
dojo.toJson(serverQuery) == dojo.toJson(this._lastServerQuery)
){
this._numRows = (this._numRows === -1) ? this._items.length : this._numRows;
fetchHandler(this._items, request, this._numRows);
}else{
var xhrFunc = this.requestMethod.toLowerCase() == "post" ? dojo.xhrPost : dojo.xhrGet;
var xhrHandler = xhrFunc({url:this.url, handleAs:"json-comment-optional", content:serverQuery, failOk: true});
request.abort = function(){
xhrHandler.cancel();
};
xhrHandler.addCallback(dojo.hitch(this, function(data){
this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
}));
xhrHandler.addErrback(function(error){
errorHandler(error, request);
});
// Generate the hash using the time in milliseconds and a randon number.
// Since Math.randon() returns something like: 0.23453463, we just remove the "0."
// probably just for esthetic reasons :-).
this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2);
this._lastServerQuery = dojo.mixin({}, serverQuery);
}
},
_filterResponse: function(data){
// summary:
// If the data from servers needs to be processed before it can be processed by this
// store, then this function should be re-implemented in subclass. This default
// implementation just return the data unchanged.
// data:
// The data received from server
return data;
},
_assertIsItem: function(/* item */ item){
// summary:
// It throws an error if item is not valid, so you can call it in every method that needs to
// throw an error when item is invalid.
// item:
// The item to test for being contained by the store.
if(!this.isItem(item)){
throw new Error(this._className+": Invalid item argument.");
}
},
_assertIsAttribute: function(/* attribute-name-string */ attribute){
// summary:
// This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
// attribute:
// The attribute to test for being contained by the store.
if(typeof attribute !== "string"){
throw new Error(this._className+": Invalid attribute argument ('"+attribute+"').");
}
},
fetchItemByIdentity: function(/* Object */ keywordArgs){
// summary:
// See dojo.data.api.Identity.fetchItemByIdentity()
// See if we have already loaded the item with that id
// In case there hasn't been a fetch yet, _itemsByIdentity is null
// and thus a fetch will be triggered below.
if(this._itemsByIdentity){
var item = this._itemsByIdentity[keywordArgs.identity];
if(!(item === undefined)){
if(keywordArgs.onItem){
var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
keywordArgs.onItem.call(scope, {i:item, r:this});
}
return;
}
}
// Otherwise we need to go remote
// Set up error handler
var _errorHandler = function(errorData, requestObject){
var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
if(keywordArgs.onError){
keywordArgs.onError.call(scope, errorData);
}
};
// Set up fetch handler
var _fetchHandler = function(items, requestObject){
var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
try{
// There is supposed to be only one result
var item = null;
if(items && items.length == 1){
item = items[0];
}
// If no item was found, item is still null and we'll
// fire the onItem event with the null here
if(keywordArgs.onItem){
keywordArgs.onItem.call(scope, item);
}
}catch(error){
if(keywordArgs.onError){
keywordArgs.onError.call(scope, error);
}
}
};
// Construct query
var request = {serverQuery:{id:keywordArgs.identity}};
// Dispatch query
this._fetchItems(request, _fetchHandler, _errorHandler);
},
getIdentity: function(/* item */ item){
// summary:
// See dojo.data.api.Identity.getIdentity()
var identifier = null;
if(this._identifier === Number){
identifier = item.n; // Number
}else{
identifier = item.i[this._identifier];
}
return identifier;
},
getIdentityAttributes: function(/* item */ item){
// summary:
// See dojo.data.api.Identity.getIdentityAttributes()
return [this._identifier];
}
}
);
}
if(!dojo._hasResource["dojox.string.Builder"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.string.Builder"] = true;
dojo.provide("dojox.string.Builder");
dojox.string.Builder = function(/*String?*/str){
// summary:
// A fast buffer for creating large strings.
//
// length: Number
// The current length of the internal string.
// N.B. the public nature of the internal buffer is no longer
// needed because the IE-specific fork is no longer needed--TRT.
var b = "";
this.length = 0;
this.append = function(/* String... */s){
// summary: Append all arguments to the end of the buffer
if(arguments.length>1){
/*
This is a loop unroll was designed specifically for Firefox;
it would seem that static index access on an Arguments
object is a LOT faster than doing dynamic index access.
Therefore, we create a buffer string and take advantage
of JS's switch fallthrough. The peformance of this method
comes very close to straight up string concatenation (+=).
If the arguments object length is greater than 9, we fall
back to standard dynamic access.
This optimization seems to have no real effect on either
Safari or Opera, so we just use it for all.
It turns out also that this loop unroll can increase performance
significantly with Internet Explorer, particularly when
as many arguments are provided as possible.
Loop unroll per suggestion from Kris Zyp, implemented by
Tom Trenka.
Note: added empty string to force a string cast if needed.
*/
var tmp="", l=arguments.length;
switch(l){
case 9: tmp=""+arguments[8]+tmp;
case 8: tmp=""+arguments[7]+tmp;
case 7: tmp=""+arguments[6]+tmp;
case 6: tmp=""+arguments[5]+tmp;
case 5: tmp=""+arguments[4]+tmp;
case 4: tmp=""+arguments[3]+tmp;
case 3: tmp=""+arguments[2]+tmp;
case 2: {
b+=""+arguments[0]+arguments[1]+tmp;
break;
}
default: {
var i=0;
while(i limit - 1)){
break;
}
}
parts.push(this.slice(lastIndex));
return parts;
}
dd.Token = function(token_type, contents){
this.token_type = token_type;
this.contents = new String(dojo.trim(contents));
this.contents.split = split;
this.split = function(){
return String.prototype.split.apply(this.contents, arguments);
}
}
dd.Token.prototype.split_contents = function(/*Integer?*/ limit){
var bit, bits = [], i = 0;
limit = limit || 999;
while(i++ < limit && (bit = smart_split_re.exec(this.contents))){
bit = bit[0];
if(bit.charAt(0) == '"' && bit.slice(-1) == '"'){
bits.push('"' + bit.slice(1, -1).replace('\\"', '"').replace('\\\\', '\\') + '"');
}else if(bit.charAt(0) == "'" && bit.slice(-1) == "'"){
bits.push("'" + bit.slice(1, -1).replace("\\'", "'").replace('\\\\', '\\') + "'");
}else{
bits.push(bit);
}
}
return bits;
}
var ddt = dd.text = {
_get: function(module, name, errorless){
// summary: Used to find both tags and filters
var params = dd.register.get(module, name.toLowerCase(), errorless);
if(!params){
if(!errorless){
throw new Error("No tag found for " + name);
}
return null;
}
var fn = params[1];
var require = params[2];
var parts;
if(fn.indexOf(":") != -1){
parts = fn.split(":");
fn = parts.pop();
}
dojo["require"](require);
var parent = dojo.getObject(require);
return parent[fn || name] || parent[name + "_"] || parent[fn + "_"];
},
getTag: function(name, errorless){
return ddt._get("tag", name, errorless);
},
getFilter: function(name, errorless){
return ddt._get("filter", name, errorless);
},
getTemplate: function(file){
return new dd.Template(ddt.getTemplateString(file));
},
getTemplateString: function(file){
return dojo._getText(file.toString()) || "";
},
_resolveLazy: function(location, sync, json){
if(sync){
if(json){
return dojo.fromJson(dojo._getText(location)) || {};
}else{
return dd.text.getTemplateString(location);
}
}else{
return dojo.xhrGet({
handleAs: (json) ? "json" : "text",
url: location
});
}
},
_resolveTemplateArg: function(arg, sync){
if(ddt._isTemplate(arg)){
if(!sync){
var d = new dojo.Deferred();
d.callback(arg);
return d;
}
return arg;
}
return ddt._resolveLazy(arg, sync);
},
_isTemplate: function(arg){
return (typeof arg == "undefined") || (typeof arg == "string" && (arg.match(/^\s*[<{]/) || arg.indexOf(" ") != -1));
},
_resolveContextArg: function(arg, sync){
if(arg.constructor == Object){
if(!sync){
var d = new dojo.Deferred;
d.callback(arg);
return d;
}
return arg;
}
return ddt._resolveLazy(arg, sync, true);
},
_re: /(?:\{\{\s*(.+?)\s*\}\}|\{%\s*(load\s*)?(.+?)\s*%\})/g,
tokenize: function(str){
return dojox.string.tokenize(str, ddt._re, ddt._parseDelims);
},
_parseDelims: function(varr, load, tag){
if(varr){
return [dd.TOKEN_VAR, varr];
}else if(load){
var parts = dojo.trim(tag).split(/\s+/g);
for(var i = 0, part; part = parts[i]; i++){
dojo["require"](part);
}
}else{
return [dd.TOKEN_BLOCK, tag];
}
}
}
dd.Template = dojo.extend(function(/*String|dojo._Url*/ template, /*Boolean*/ isString){
// template:
// The string or location of the string to
// use as a template
var str = isString ? template : ddt._resolveTemplateArg(template, true) || "";
var tokens = ddt.tokenize(str);
var parser = new dd._Parser(tokens);
this.nodelist = parser.parse();
},
{
update: function(node, context){
// node: DOMNode|String|dojo.NodeList
// A node reference or set of nodes
// context: dojo._Url|String|Object
// The context object or location
return ddt._resolveContextArg(context).addCallback(this, function(contextObject){
var content = this.render(new dd._Context(contextObject));
if(node.forEach){
node.forEach(function(item){
item.innerHTML = content;
});
}else{
dojo.byId(node).innerHTML = content;
}
return this;
});
},
render: function(context, /*concatenatable?*/ buffer){
buffer = buffer || this.getBuffer();
context = context || new dd._Context({});
return this.nodelist.render(context, buffer) + "";
},
getBuffer: function(){
return new dojox.string.Builder();
}
});
var qfRe = /\{\{\s*(.+?)\s*\}\}/g;
dd.quickFilter = function(str){
if(!str){
return new dd._NodeList();
}
if(str.indexOf("{%") == -1){
return new dd._QuickNodeList(dojox.string.tokenize(str, qfRe, function(token){
return new dd._Filter(token);
}));
}
}
dd._QuickNodeList = dojo.extend(function(contents){
this.contents = contents;
},
{
render: function(context, buffer){
for(var i=0, l=this.contents.length; i/g;
var escapeqt = /'/g;
var escapedblqt = /"/g;
dd._base.escape = function(value){
// summary: Escapes a string's HTML
return dd.mark_safe(value.replace(escapeamp, '&').replace(escapelt, '<').replace(escapegt, '>').replace(escapedblqt, '"').replace(escapeqt, '''));
}
dd._base.safe = function(value){
if(typeof value == "string"){
value = new String(value);
}
if(typeof value == "object"){
value.safe = true;
}
return value;
}
dd.mark_safe = dd._base.safe;
dd.register.tags("dojox.dtl.tag", {
"date": ["now"],
"logic": ["if", "for", "ifequal", "ifnotequal"],
"loader": ["extends", "block", "include", "load", "ssi"],
"misc": ["comment", "debug", "filter", "firstof", "spaceless", "templatetag", "widthratio", "with"],
"loop": ["cycle", "ifchanged", "regroup"]
});
dd.register.filters("dojox.dtl.filter", {
"dates": ["date", "time", "timesince", "timeuntil"],
"htmlstrings": ["linebreaks", "linebreaksbr", "removetags", "striptags"],
"integers": ["add", "get_digit"],
"lists": ["dictsort", "dictsortreversed", "first", "join", "length", "length_is", "random", "slice", "unordered_list"],
"logic": ["default", "default_if_none", "divisibleby", "yesno"],
"misc": ["filesizeformat", "pluralize", "phone2numeric", "pprint"],
"strings": ["addslashes", "capfirst", "center", "cut", "fix_ampersands", "floatformat", "iriencode", "linenumbers", "ljust", "lower", "make_list", "rjust", "slugify", "stringformat", "title", "truncatewords", "truncatewords_html", "upper", "urlencode", "urlize", "urlizetrunc", "wordcount", "wordwrap"]
});
dd.register.filters("dojox.dtl", {
"_base": ["escape", "safe"]
});
})();
}
if(!dojo._hasResource["dojox.dtl.filter.htmlstrings"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.dtl.filter.htmlstrings"] = true;
dojo.provide("dojox.dtl.filter.htmlstrings");
dojo.mixin(dojox.dtl.filter.htmlstrings, {
_linebreaksrn: /(\r\n|\n\r)/g,
_linebreaksn: /\n{2,}/g,
_linebreakss: /(^\s+|\s+$)/g,
_linebreaksbr: /\n/g,
_removetagsfind: /[a-z0-9]+/g,
_striptags: /<[^>]*?>/g,
linebreaks: function(value){
// summary: Converts newlines into and s
var output = [];
var dh = dojox.dtl.filter.htmlstrings;
value = value.replace(dh._linebreaksrn, "\n");
var parts = value.split(dh._linebreaksn);
for(var i = 0; i < parts.length; i++){
var part = parts[i].replace(dh._linebreakss, "").replace(dh._linebreaksbr, " ");
output.push("
" + part + "
");
}
return output.join("\n\n");
},
linebreaksbr: function(value){
// summary: Converts newlines into s
var dh = dojox.dtl.filter.htmlstrings;
return value.replace(dh._linebreaksrn, "\n").replace(dh._linebreaksbr, " ");
},
removetags: function(value, arg){
// summary: Removes a space separated list of [X]HTML tags from the output"
var dh = dojox.dtl.filter.htmlstrings;
var tags = [];
var group;
while(group = dh._removetagsfind.exec(arg)){
tags.push(group[0]);
}
tags = "(" + tags.join("|") + ")";
return value.replace(new RegExp("?\s*" + tags + "\s*[^>]*>", "gi"), "");
},
striptags: function(value){
// summary: Strips all [X]HTML tags
return value.replace(dojox.dtl.filter.htmlstrings._striptags, "");
}
});
}
if(!dojo._hasResource["dojox.string.sprintf"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.string.sprintf"] = true;
dojo.provide("dojox.string.sprintf");
dojox.string.sprintf = function(/*String*/ format, /*mixed...*/ filler){
for(var args = [], i = 1; i < arguments.length; i++){
args.push(arguments[i]);
}
var formatter = new dojox.string.sprintf.Formatter(format);
return formatter.format.apply(formatter, args);
}
dojox.string.sprintf.Formatter = function(/*String*/ format){
var tokens = [];
this._mapped = false;
this._format = format;
this._tokens = dojox.string.tokenize(format, this._re, this._parseDelim, this);
}
dojo.extend(dojox.string.sprintf.Formatter, {
_re: /\%(?:\(([\w_]+)\)|([1-9]\d*)\$)?([0 +\-\#]*)(\*|\d+)?(\.)?(\*|\d+)?[hlL]?([\%scdeEfFgGiouxX])/g,
_parseDelim: function(mapping, intmapping, flags, minWidth, period, precision, specifier){
if(mapping){
this._mapped = true;
}
return {
mapping: mapping,
intmapping: intmapping,
flags: flags,
_minWidth: minWidth, // May be dependent on parameters
period: period,
_precision: precision, // May be dependent on parameters
specifier: specifier
};
},
_specifiers: {
b: {
base: 2,
isInt: true
},
o: {
base: 8,
isInt: true
},
x: {
base: 16,
isInt: true
},
X: {
extend: ["x"],
toUpper: true
},
d: {
base: 10,
isInt: true
},
i: {
extend: ["d"]
},
u: {
extend: ["d"],
isUnsigned: true
},
c: {
setArg: function(token){
if(!isNaN(token.arg)){
var num = parseInt(token.arg);
if(num < 0 || num > 127){
throw new Error("invalid character code passed to %c in sprintf");
}
token.arg = isNaN(num) ? "" + num : String.fromCharCode(num);
}
}
},
s: {
setMaxWidth: function(token){
token.maxWidth = (token.period == ".") ? token.precision : -1;
}
},
e: {
isDouble: true,
doubleNotation: "e"
},
E: {
extend: ["e"],
toUpper: true
},
f: {
isDouble: true,
doubleNotation: "f"
},
F: {
extend: ["f"]
},
g: {
isDouble: true,
doubleNotation: "g"
},
G: {
extend: ["g"],
toUpper: true
}
},
format: function(/*mixed...*/ filler){
if(this._mapped && typeof filler != "object"){
throw new Error("format requires a mapping");
}
var str = "";
var position = 0;
for(var i = 0, token; i < this._tokens.length; i++){
token = this._tokens[i];
if(typeof token == "string"){
str += token;
}else{
if(this._mapped){
if(typeof filler[token.mapping] == "undefined"){
throw new Error("missing key " + token.mapping);
}
token.arg = filler[token.mapping];
}else{
if(token.intmapping){
var position = parseInt(token.intmapping) - 1;
}
if(position >= arguments.length){
throw new Error("got " + arguments.length + " printf arguments, insufficient for '" + this._format + "'");
}
token.arg = arguments[position++];
}
if(!token.compiled){
token.compiled = true;
token.sign = "";
token.zeroPad = false;
token.rightJustify = false;
token.alternative = false;
var flags = {};
for(var fi = token.flags.length; fi--;){
var flag = token.flags.charAt(fi);
flags[flag] = true;
switch(flag){
case " ":
token.sign = " ";
break;
case "+":
token.sign = "+";
break;
case "0":
token.zeroPad = (flags["-"]) ? false : true;
break;
case "-":
token.rightJustify = true;
token.zeroPad = false;
break;
case "\#":
token.alternative = true;
break;
default:
throw Error("bad formatting flag '" + token.flags.charAt(fi) + "'");
}
}
token.minWidth = (token._minWidth) ? parseInt(token._minWidth) : 0;
token.maxWidth = -1;
token.toUpper = false;
token.isUnsigned = false;
token.isInt = false;
token.isDouble = false;
token.precision = 1;
if(token.period == '.'){
if(token._precision){
token.precision = parseInt(token._precision);
}else{
token.precision = 0;
}
}
var mixins = this._specifiers[token.specifier];
if(typeof mixins == "undefined"){
throw new Error("unexpected specifier '" + token.specifier + "'");
}
if(mixins.extend){
dojo.mixin(mixins, this._specifiers[mixins.extend]);
delete mixins.extend;
}
dojo.mixin(token, mixins);
}
if(typeof token.setArg == "function"){
token.setArg(token);
}
if(typeof token.setMaxWidth == "function"){
token.setMaxWidth(token);
}
if(token._minWidth == "*"){
if(this._mapped){
throw new Error("* width not supported in mapped formats");
}
token.minWidth = parseInt(arguments[position++]);
if(isNaN(token.minWidth)){
throw new Error("the argument for * width at position " + position + " is not a number in " + this._format);
}
// negative width means rightJustify
if (token.minWidth < 0) {
token.rightJustify = true;
token.minWidth = -token.minWidth;
}
}
if(token._precision == "*" && token.period == "."){
if(this._mapped){
throw new Error("* precision not supported in mapped formats");
}
token.precision = parseInt(arguments[position++]);
if(isNaN(token.precision)){
throw Error("the argument for * precision at position " + position + " is not a number in " + this._format);
}
// negative precision means unspecified
if (token.precision < 0) {
token.precision = 1;
token.period = '';
}
}
if(token.isInt){
// a specified precision means no zero padding
if(token.period == '.'){
token.zeroPad = false;
}
this.formatInt(token);
}else if(token.isDouble){
if(token.period != '.'){
token.precision = 6;
}
this.formatDouble(token);
}
this.fitField(token);
str += "" + token.arg;
}
}
return str;
},
_zeros10: '0000000000',
_spaces10: ' ',
formatInt: function(token) {
var i = parseInt(token.arg);
if(!isFinite(i)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
// allow this only if arg is number
if(typeof token.arg != "number"){
throw new Error("format argument '" + token.arg + "' not an integer; parseInt returned " + i);
}
//return '' + i;
i = 0;
}
// if not base 10, make negatives be positive
// otherwise, (-10).toString(16) is '-a' instead of 'fffffff6'
if(i < 0 && (token.isUnsigned || token.base != 10)){
i = 0xffffffff + i + 1;
}
if(i < 0){
token.arg = (- i).toString(token.base);
this.zeroPad(token);
token.arg = "-" + token.arg;
}else{
token.arg = i.toString(token.base);
// need to make sure that argument 0 with precision==0 is formatted as ''
if(!i && !token.precision){
token.arg = "";
}else{
this.zeroPad(token);
}
if(token.sign){
token.arg = token.sign + token.arg;
}
}
if(token.base == 16){
if(token.alternative){
token.arg = '0x' + token.arg;
}
token.arg = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
}
if(token.base == 8){
if(token.alternative && token.arg.charAt(0) != '0'){
token.arg = '0' + token.arg;
}
}
},
formatDouble: function(token) {
var f = parseFloat(token.arg);
if(!isFinite(f)){ // isNaN(f) || f == Number.POSITIVE_INFINITY || f == Number.NEGATIVE_INFINITY)
// allow this only if arg is number
if(typeof token.arg != "number"){
throw new Error("format argument '" + token.arg + "' not a float; parseFloat returned " + f);
}
// C99 says that for 'f':
// infinity -> '[-]inf' or '[-]infinity' ('[-]INF' or '[-]INFINITY' for 'F')
// NaN -> a string starting with 'nan' ('NAN' for 'F')
// this is not commonly implemented though.
//return '' + f;
f = 0;
}
switch(token.doubleNotation) {
case 'e': {
token.arg = f.toExponential(token.precision);
break;
}
case 'f': {
token.arg = f.toFixed(token.precision);
break;
}
case 'g': {
// C says use 'e' notation if exponent is < -4 or is >= prec
// ECMAScript for toPrecision says use exponential notation if exponent is >= prec,
// though step 17 of toPrecision indicates a test for < -6 to force exponential.
if(Math.abs(f) < 0.0001){
//print("forcing exponential notation for f=" + f);
token.arg = f.toExponential(token.precision > 0 ? token.precision - 1 : token.precision);
}else{
token.arg = f.toPrecision(token.precision);
}
// In C, unlike 'f', 'gG' removes trailing 0s from fractional part, unless alternative format flag ("#").
// But ECMAScript formats toPrecision as 0.00100000. So remove trailing 0s.
if(!token.alternative){
//print("replacing trailing 0 in '" + s + "'");
token.arg = token.arg.replace(/(\..*[^0])0*/, "$1");
// if fractional part is entirely 0, remove it and decimal point
token.arg = token.arg.replace(/\.0*e/, 'e').replace(/\.0$/,'');
}
break;
}
default: throw new Error("unexpected double notation '" + token.doubleNotation + "'");
}
// C says that exponent must have at least two digits.
// But ECMAScript does not; toExponential results in things like "1.000000e-8" and "1.000000e+8".
// Note that s.replace(/e([\+\-])(\d)/, "e$10$2") won't work because of the "$10" instead of "$1".
// And replace(re, func) isn't supported on IE50 or Safari1.
token.arg = token.arg.replace(/e\+(\d)$/, "e+0$1").replace(/e\-(\d)$/, "e-0$1");
// Ensure a '0' before the period.
// Opera implements (0.001).toString() as '0.001', but (0.001).toFixed(1) is '.001'
if(dojo.isOpera){
token.arg = token.arg.replace(/^\./, '0.');
}
// if alt, ensure a decimal point
if(token.alternative){
token.arg = token.arg.replace(/^(\d+)$/,"$1.");
token.arg = token.arg.replace(/^(\d+)e/,"$1.e");
}
if(f >= 0 && token.sign){
token.arg = token.sign + token.arg;
}
token.arg = token.toUpper ? token.arg.toUpperCase() : token.arg.toLowerCase();
},
zeroPad: function(token, /*Int*/ length) {
length = (arguments.length == 2) ? length : token.precision;
if(typeof token.arg != "string"){
token.arg = "" + token.arg;
}
var tenless = length - 10;
while(token.arg.length < tenless){
token.arg = (token.rightJustify) ? token.arg + this._zeros10 : this._zeros10 + token.arg;
}
var pad = length - token.arg.length;
token.arg = (token.rightJustify) ? token.arg + this._zeros10.substring(0, pad) : this._zeros10.substring(0, pad) + token.arg;
},
fitField: function(token) {
if(token.maxWidth >= 0 && token.arg.length > token.maxWidth){
return token.arg.substring(0, token.maxWidth);
}
if(token.zeroPad){
this.zeroPad(token, token.minWidth);
return;
}
this.spacePad(token);
},
spacePad: function(token, /*Int*/ length) {
length = (arguments.length == 2) ? length : token.minWidth;
if(typeof token.arg != 'string'){
token.arg = '' + token.arg;
}
var tenless = length - 10;
while(token.arg.length < tenless){
token.arg = (token.rightJustify) ? token.arg + this._spaces10 : this._spaces10 + token.arg;
}
var pad = length - token.arg.length;
token.arg = (token.rightJustify) ? token.arg + this._spaces10.substring(0, pad) : this._spaces10.substring(0, pad) + token.arg;
}
});
}
if(!dojo._hasResource["dojox.dtl.filter.strings"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.dtl.filter.strings"] = true;
dojo.provide("dojox.dtl.filter.strings");
dojo.mixin(dojox.dtl.filter.strings, {
_urlquote: function(/*String*/ url, /*String?*/ safe){
if(!safe){
safe = "/";
}
return dojox.string.tokenize(url, /([^\w-_.])/g, function(token){
if(safe.indexOf(token) == -1){
if(token == " "){
return "+";
}else{
return "%" + token.charCodeAt(0).toString(16).toUpperCase();
}
}
return token;
}).join("");
},
addslashes: function(value){
// summary: Adds slashes - useful for passing strings to JavaScript, for example.
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'");
},
capfirst: function(value){
// summary: Capitalizes the first character of the value
value = "" + value;
return value.charAt(0).toUpperCase() + value.substring(1);
},
center: function(value, arg){
// summary: Centers the value in a field of a given width
arg = arg || value.length;
value = value + "";
var diff = arg - value.length;
if(diff % 2){
value = value + " ";
diff -= 1;
}
for(var i = 0; i < diff; i += 2){
value = " " + value + " ";
}
return value;
},
cut: function(value, arg){
// summary: Removes all values of arg from the given string
arg = arg + "" || "";
value = value + "";
return value.replace(new RegExp(arg, "g"), "");
},
_fix_ampersands: /&(?!(\w+|#\d+);)/g,
fix_ampersands: function(value){
// summary: Replaces ampersands with ``&`` entities
return value.replace(dojox.dtl.filter.strings._fix_ampersands, "&");
},
floatformat: function(value, arg){
// summary: Format a number according to arg
// description:
// If called without an argument, displays a floating point
// number as 34.2 -- but only if there's a point to be displayed.
// With a positive numeric argument, it displays that many decimal places
// always.
// With a negative numeric argument, it will display that many decimal
// places -- but only if there's places to be displayed.
arg = parseInt(arg || -1, 10);
value = parseFloat(value);
var m = value - value.toFixed(0);
if(!m && arg < 0){
return value.toFixed();
}
value = value.toFixed(Math.abs(arg));
return (arg < 0) ? parseFloat(value) + "" : value;
},
iriencode: function(value){
return dojox.dtl.filter.strings._urlquote(value, "/#%[]=:;$&()+,!");
},
linenumbers: function(value){
// summary: Displays text with line numbers
var df = dojox.dtl.filter;
var lines = value.split("\n");
var output = [];
var width = (lines.length + "").length;
for(var i = 0, line; i < lines.length; i++){
line = lines[i];
output.push(df.strings.ljust(i + 1, width) + ". " + dojox.dtl._base.escape(line));
}
return output.join("\n");
},
ljust: function(value, arg){
value = value + "";
arg = parseInt(arg, 10);
while(value.length < arg){
value = value + " ";
}
return value;
},
lower: function(value){
// summary: Converts a string into all lowercase
return (value + "").toLowerCase();
},
make_list: function(value){
// summary:
// Returns the value turned into a list. For an integer, it's a list of
// digits. For a string, it's a list of characters.
var output = [];
if(typeof value == "number"){
value = value + "";
}
if(value.charAt){
for(var i = 0; i < value.length; i++){
output.push(value.charAt(i));
}
return output;
}
if(typeof value == "object"){
for(var key in value){
output.push(value[key]);
}
return output;
}
return [];
},
rjust: function(value, arg){
value = value + "";
arg = parseInt(arg, 10);
while(value.length < arg){
value = " " + value;
}
return value;
},
slugify: function(value){
// summary: Converts to lowercase, removes
// non-alpha chars and converts spaces to hyphens
value = value.replace(/[^\w\s-]/g, "").toLowerCase();
return value.replace(/[\-\s]+/g, "-");
},
_strings: {},
stringformat: function(value, arg){
// summary:
// Formats the variable according to the argument, a string formatting specifier.
// This specifier uses Python string formating syntax, with the exception that
// the leading "%" is dropped.
arg = "" + arg;
var strings = dojox.dtl.filter.strings._strings;
if(!strings[arg]){
strings[arg] = new dojox.string.sprintf.Formatter("%" + arg);
}
return strings[arg].format(value);
},
title: function(value){
// summary: Converts a string into titlecase
var last, title = "";
for(var i = 0, current; i < value.length; i++){
current = value.charAt(i);
if(last == " " || last == "\n" || last == "\t" || !last){
title += current.toUpperCase();
}else{
title += current.toLowerCase();
}
last = current;
}
return title;
},
_truncatewords: /[ \n\r\t]/,
truncatewords: function(value, arg){
// summary: Truncates a string after a certain number of words
// arg: Integer
// Number of words to truncate after
arg = parseInt(arg, 10);
if(!arg){
return value;
}
for(var i = 0, j = value.length, count = 0, current, last; i < value.length; i++){
current = value.charAt(i);
if(dojox.dtl.filter.strings._truncatewords.test(last)){
if(!dojox.dtl.filter.strings._truncatewords.test(current)){
++count;
if(count == arg){
return value.substring(0, j + 1);
}
}
}else if(!dojox.dtl.filter.strings._truncatewords.test(current)){
j = i;
}
last = current;
}
return value;
},
_truncate_words: /(&.*?;|<.*?>|(\w[\w\-]*))/g,
_truncate_tag: /<(\/)?([^ ]+?)(?: (\/)| .*?)?>/,
_truncate_singlets: { br: true, col: true, link: true, base: true, img: true, param: true, area: true, hr: true, input: true },
truncatewords_html: function(value, arg){
arg = parseInt(arg, 10);
if(arg <= 0){
return "";
}
var strings = dojox.dtl.filter.strings;
var words = 0;
var open = [];
var output = dojox.string.tokenize(value, strings._truncate_words, function(all, word){
if(word){
// It's an actual non-HTML word
++words;
if(words < arg){
return word;
}else if(words == arg){
return word + " ...";
}
}
// Check for tag
var tag = all.match(strings._truncate_tag);
if(!tag || words >= arg){
// Don't worry about non tags or tags after our truncate point
return;
}
var closing = tag[1];
var tagname = tag[2].toLowerCase();
var selfclosing = tag[3];
if(closing || strings._truncate_singlets[tagname]){
}else if(closing){
var i = dojo.indexOf(open, tagname);
if(i != -1){
open = open.slice(i + 1);
}
}else{
open.unshift(tagname);
}
return all;
}).join("");
output = output.replace(/\s+$/g, "");
for(var i = 0, tag; tag = open[i]; i++){
output += "" + tag + ">";
}
return output;
},
upper: function(value){
return value.toUpperCase();
},
urlencode: function(value){
return dojox.dtl.filter.strings._urlquote(value);
},
_urlize: /^((?:[(>]|<)*)(.*?)((?:[.,)>\n]|>)*)$/,
_urlize2: /^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/,
urlize: function(value){
return dojox.dtl.filter.strings.urlizetrunc(value);
},
urlizetrunc: function(value, arg){
arg = parseInt(arg);
return dojox.string.tokenize(value, /(\S+)/g, function(word){
var matches = dojox.dtl.filter.strings._urlize.exec(word);
if(!matches){
return word;
}
var lead = matches[1];
var middle = matches[2];
var trail = matches[3];
var startsWww = middle.indexOf("www.") == 0;
var hasAt = middle.indexOf("@") != -1;
var hasColon = middle.indexOf(":") != -1;
var startsHttp = middle.indexOf("http://") == 0;
var startsHttps = middle.indexOf("https://") == 0;
var firstAlpha = /[a-zA-Z0-9]/.test(middle.charAt(0));
var last4 = middle.substring(middle.length - 4);
var trimmed = middle;
if(arg > 3){
trimmed = trimmed.substring(0, arg - 3) + "...";
}
if(startsWww || (!hasAt && !startsHttp && middle.length && firstAlpha && (last4 == ".org" || last4 == ".net" || last4 == ".com"))){
return '' + trimmed + ' ';
}else if(startsHttp || startsHttps){
return '' + trimmed + ' ';
}else if(hasAt && !startsWww && !hasColon && dojox.dtl.filter.strings._urlize2.test(middle)){
return '' + middle + ' ';
}
return word;
}).join("");
},
wordcount: function(value){
value = dojo.trim(value);
if(!value){ return 0; }
return value.split(/\s+/g).length;
},
wordwrap: function(value, arg){
arg = parseInt(arg);
// summary: Wraps words at specified line length
var output = [];
var parts = value.split(/\s+/g);
if(parts.length){
var word = parts.shift();
output.push(word);
var pos = word.length - word.lastIndexOf("\n") - 1;
for(var i = 0; i < parts.length; i++){
word = parts[i];
if(word.indexOf("\n") != -1){
var lines = word.split(/\n/g);
}else{
var lines = [word];
}
pos += lines[0].length + 1;
if(arg && pos > arg){
output.push("\n");
pos = lines[lines.length - 1].length;
}else{
output.push(" ");
if(lines.length > 1){
pos = lines[lines.length - 1].length;
}
}
output.push(word);
}
}
return output.join("");
}
});
}
if(!dojo._hasResource["dojox.fx._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.fx._base"] = true;
dojo.provide("dojox.fx._base");
// summary: Experimental and extended Animations beyond Dojo Core / Base functionality.
// Provides advanced Lines, Animations, and convenience aliases.
dojo.mixin(dojox.fx, {
// anim: Function
// Alias of `dojo.anim` - the shorthand `dojo.animateProperty` with auto-play
anim: dojo.anim,
// animateProperty: Function
// Alias of `dojo.animateProperty` - animate any CSS property
animateProperty: dojo.animateProperty,
// fadeTo: Function
// Fade an element from an opacity to an opacity.
// Omit `start:` property to detect. `end:` property is required.
// Ultimately an alias to `dojo._fade`
fadeTo: dojo._fade,
// fadeIn: Function
// Alias of `dojo.fadeIn` - Fade a node in.
fadeIn: dojo.fadeIn,
// fadeOut: Function
// Alias of `dojo.fadeOut` - Fades a node out.
fadeOut: dojo.fadeOut,
// combine: Function
// Alias of `dojo.fx.combine` - Run an array of animations in parallel
combine: dojo.fx.combine,
// chain: Function
// Alias of `dojo.fx.chain` - Run an array of animations in sequence
chain: dojo.fx.chain,
// slideTo: Function
// Alias of `dojo.fx.slideTo` - Slide a node to a defined top/left coordinate
slideTo: dojo.fx.slideTo,
// wipeIn: Function
// Alias of `dojo.fx.wipeIn` - Wipe a node to visible
wipeIn: dojo.fx.wipeIn,
// wipeOut: Function
// Alias of `dojo.fx.wipeOut` - Wipe a node to non-visible
wipeOut: dojo.fx.wipeOut
});
dojox.fx.sizeTo = function(/* Object */args){
// summary:
// Creates an animation that will size a node
//
// description:
// Returns an animation that will size the target node
// defined in args Object about it's center to
// a width and height defined by (args.width, args.height),
// supporting an optional method: chain||combine mixin
// (defaults to chain).
//
// - works best on absolutely or relatively positioned elements
//
// example:
// | // size #myNode to 400px x 200px over 1 second
// | dojo.fx.sizeTo({
// | node:'myNode',
// | duration: 1000,
// | width: 400,
// | height: 200,
// | method: "combine"
// | }).play();
//
var node = args.node = dojo.byId(args.node),
abs = "absolute";
var method = args.method || "chain";
if(!args.duration){ args.duration = 500; } // default duration needed
if(method == "chain"){ args.duration = Math.floor(args.duration / 2); }
var top, newTop, left, newLeft, width, height = null;
var init = (function(n){
return function(){
var cs = dojo.getComputedStyle(n),
pos = cs.position,
w = cs.width,
h = cs.height
;
top = (pos == abs ? n.offsetTop : parseInt(cs.top) || 0);
left = (pos == abs ? n.offsetLeft : parseInt(cs.left) || 0);
width = (w == "auto" ? 0 : parseInt(w));
height = (h == "auto" ? 0 : parseInt(h));
newLeft = left - Math.floor((args.width - width) / 2);
newTop = top - Math.floor((args.height - height) / 2);
if(pos != abs && pos != 'relative'){
var ret = dojo.coords(n, true);
top = ret.y;
left = ret.x;
n.style.position = abs;
n.style.top = top + "px";
n.style.left = left + "px";
}
}
})(node);
var anim1 = dojo.animateProperty(dojo.mixin({
properties: {
height: function(){
init();
return { end: args.height || 0, start: height };
},
top: function(){
return { start: top, end: newTop };
}
}
}, args));
var anim2 = dojo.animateProperty(dojo.mixin({
properties: {
width: function(){
return { start: width, end: args.width || 0 }
},
left: function(){
return { start: left, end: newLeft }
}
}
}, args));
var anim = dojo.fx[(args.method == "combine" ? "combine" : "chain")]([anim1, anim2]);
return anim; // dojo.Animation
};
dojox.fx.slideBy = function(/* Object */args){
// summary:
// Returns an animation to slide a node by a defined offset.
//
// description:
// Returns an animation that will slide a node (args.node) from it's
// current position to it's current posision plus the numbers defined
// in args.top and args.left. standard dojo.fx mixin's apply.
//
// example:
// | // slide domNode 50px down, and 22px left
// | dojox.fx.slideBy({
// | node: domNode, duration:400,
// | top: 50, left: -22
// | }).play();
var node = args.node = dojo.byId(args.node),
top, left;
var init = (function(n){
return function(){
var cs = dojo.getComputedStyle(n);
var pos = cs.position;
top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
if(pos != 'absolute' && pos != 'relative'){
var ret = dojo.coords(n, true);
top = ret.y;
left = ret.x;
n.style.position = "absolute";
n.style.top = top + "px";
n.style.left = left + "px";
}
}
})(node);
init();
var _anim = dojo.animateProperty(dojo.mixin({
properties: {
// FIXME: is there a way to update the _Line after creation?
// null start values allow chaining to work, animateProperty will
// determine them for us (except in ie6? -- ugh)
top: top + (args.top || 0),
left: left + (args.left || 0)
}
}, args));
dojo.connect(_anim, "beforeBegin", _anim, init);
return _anim; // dojo.Animation
};
dojox.fx.crossFade = function(/* Object */args){
// summary:
// Returns an animation cross fading two element simultaneously
//
// args:
// args.nodes: Array - two element array of domNodes, or id's
//
// all other standard animation args mixins apply. args.node ignored.
//
// simple check for which node is visible, maybe too simple?
var node1 = args.nodes[0] = dojo.byId(args.nodes[0]),
op1 = dojo.style(node1,"opacity"),
node2 = args.nodes[1] = dojo.byId(args.nodes[1]),
op2 = dojo.style(node2, "opacity")
;
var _anim = dojo.fx.combine([
dojo[(op1 == 0 ? "fadeIn" : "fadeOut")](dojo.mixin({
node: node1
},args)),
dojo[(op1 == 0 ? "fadeOut" : "fadeIn")](dojo.mixin({
node: node2
},args))
]);
return _anim; // dojo.Animation
};
dojox.fx.highlight = function(/*Object*/ args){
// summary:
// Highlight a node
//
// description:
// Returns an animation that sets the node background to args.color
// then gradually fades back the original node background color
//
// example:
// | dojox.fx.highlight({ node:"foo" }).play();
var node = args.node = dojo.byId(args.node);
args.duration = args.duration || 400;
// Assign default color light yellow
var startColor = args.color || '#ffff99',
endColor = dojo.style(node, "backgroundColor")
;
// safari "fix"
// safari reports rgba(0, 0, 0, 0) (black) as transparent color, while
// other browsers return "transparent", rendered as white by default by
// dojo.Color; now dojo.Color maps "transparent" to
// djConfig.transparentColor ([r, g, b]), if present; so we can use
// the color behind the effect node
if(endColor == "rgba(0, 0, 0, 0)"){
endColor = "transparent";
}
var anim = dojo.animateProperty(dojo.mixin({
properties: {
backgroundColor: { start: startColor, end: endColor }
}
}, args));
if(endColor == "transparent"){
dojo.connect(anim, "onEnd", anim, function(){
node.style.backgroundColor = endColor;
});
}
return anim; // dojo.Animation
};
dojox.fx.wipeTo = function(/*Object*/ args){
// summary:
// Animate a node wiping to a specific width or height
//
// description:
// Returns an animation that will expand the
// node defined in 'args' object from it's current to
// the height or width value given by the args object.
//
// default to height:, so leave height null and specify width:
// to wipeTo a width. note: this may be deprecated by a
//
// Note that the final value should not include
// units and should be an integer. Thus a valid args object
// would look something like this:
//
// | dojox.fx.wipeTo({ node: "nodeId", height: 200 }).play();
//
// Node must have no margin/border/padding, so put another
// node inside your target node for additional styling.
args.node = dojo.byId(args.node);
var node = args.node, s = node.style;
var dir = (args.width ? "width" : "height"),
endVal = args[dir],
props = {}
;
props[dir] = {
// wrapped in functions so we wait till the last second to query (in case value has changed)
start: function(){
// start at current [computed] height, but use 1px rather than 0
// because 0 causes IE to display the whole panel
s.overflow = "hidden";
if(s.visibility == "hidden" || s.display == "none"){
s[dir] = "1px";
s.display = "";
s.visibility = "";
return 1;
}else{
var now = dojo.style(node,dir);
return Math.max(now, 1);
}
},
end: endVal
};
var anim = dojo.animateProperty(dojo.mixin({ properties: props }, args));
return anim; // dojo.Animation
};
}
if(!dojo._hasResource["dojox.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.fx"] = true;
dojo.provide("dojox.fx");
}
if(!dojo._hasResource["dojox.form.RangeSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.form.RangeSlider"] = true;
dojo.provide("dojox.form.RangeSlider");
(function(){
// make these functions once:
var sortReversed = function(a, b){ return b - a; },
sortForward = function(a, b){ return a - b; }
;
dojo.declare("dojox.form._RangeSliderMixin", null, {
value: [0,100],
postMixInProperties: function(){
this.inherited(arguments);
this.value = dojo.map(this.value, function(i){ return parseInt(i, 10); });
},
postCreate: function(){
this.inherited(arguments);
// we sort the values!
// TODO: re-think, how to set the value
this.value.sort(this._isReversed() ? sortReversed : sortForward);
// define a custom constructor for a SliderMoverMax that points back to me
var _self = this;
var mover = dojo.declare(dijit.form._SliderMoverMax, {
constructor: function(){
this.widget = _self;
}
});
this._movableMax = new dojo.dnd.Moveable(this.sliderHandleMax,{ mover: mover });
dijit.setWaiState(this.focusNodeMax, "valuemin", this.minimum);
dijit.setWaiState(this.focusNodeMax, "valuemax", this.maximum);
// a dnd for the bar!
var barMover = dojo.declare(dijit.form._SliderBarMover, {
constructor: function(){
this.widget = _self;
}
});
this._movableBar = new dojo.dnd.Moveable(this.progressBar,{ mover: barMover });
},
destroy: function(){
this.inherited(arguments);
this._movableMax.destroy();
this._movableBar.destroy();
},
_onKeyPress: function(/*Event*/ e){
if(this.disabled || this.readOnly || e.altKey || e.ctrlKey){ return; }
var useMaxValue = e.target === this.sliderHandleMax;
var barFocus = e.target === this.progressBar;
var k = dojo.delegate(dojo.keys, this.isLeftToRight() ? {PREV_ARROW: dojo.keys.LEFT_ARROW, NEXT_ARROW: dojo.keys.RIGHT_ARROW}
: {PREV_ARROW: dojo.keys.RIGHT_ARROW, NEXT_ARROW: dojo.keys.LEFT_ARROW});
var delta = 0;
var down = false;
switch(e.keyCode){
case k.HOME : this._setValueAttr(this.minimum, true, useMaxValue);dojo.stopEvent(e);return;
case k.END : this._setValueAttr(this.maximum, true, useMaxValue);dojo.stopEvent(e);return;
case k.PREV_ARROW :
case k.DOWN_ARROW : down = true;
case k.NEXT_ARROW :
case k.UP_ARROW : delta = 1; break;
case k.PAGE_DOWN : down = true;
case k.PAGE_UP : delta = this.pageIncrement; break;
default : this.inherited(arguments);return;
}
if(down){delta = -delta;}
if(delta){
if(barFocus){
this._bumpValue([
{ change: delta, useMaxValue: false },
{ change: delta, useMaxValue: true }
]);
}else{
this._bumpValue(delta, useMaxValue);
}
dojo.stopEvent(e);
}
},
_onHandleClickMax: function(e){
if(this.disabled || this.readOnly){ return; }
if(!dojo.isIE){
// make sure you get focus when dragging the handle
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
dijit.focus(this.sliderHandleMax);
}
dojo.stopEvent(e);
},
_onClkIncBumper: function(){
this._setValueAttr(this._descending === false ? this.minimum : this.maximum, true, true);
},
_bumpValue: function(signedChange, useMaxValue){
// we pass an array to _setValueAttr when signedChange is an array
var value = dojo.isArray(signedChange) ? [
this._getBumpValue(signedChange[0].change, signedChange[0].useMaxValue),
this._getBumpValue(signedChange[1].change, signedChange[1].useMaxValue)
]
: this._getBumpValue(signedChange, useMaxValue)
this._setValueAttr(value, true, useMaxValue);
},
_getBumpValue: function(signedChange, useMaxValue){
var idx = useMaxValue ? 1 : 0;
if( this._isReversed() ) {
idx = 1 - idx;
}
var s = dojo.getComputedStyle(this.sliderBarContainer),
c = dojo._getContentBox(this.sliderBarContainer, s),
count = this.discreteValues,
myValue = this.value[idx]
;
if(count <= 1 || count == Infinity){ count = c[this._pixelCount]; }
count--;
var value = (myValue - this.minimum) * count / (this.maximum - this.minimum) + signedChange;
if(value < 0){ value = 0; }
if(value > count){ value = count; }
return value * (this.maximum - this.minimum) / count + this.minimum;
},
_onBarClick: function(e){
if(this.disabled || this.readOnly){ return; }
if(!dojo.isIE){
// make sure you get focus when dragging the handle
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
dijit.focus(this.progressBar);
}
dojo.stopEvent(e);
},
_onRemainingBarClick: function(e){
if(this.disabled || this.readOnly){ return; }
if(!dojo.isIE){
// make sure you get focus when dragging the handle
// (but don't do on IE because it causes a flicker on mouse up (due to blur then focus)
dijit.focus(this.progressBar);
}
// now we set the min/max-value of the slider!
var abspos = dojo.coords(this.sliderBarContainer, true),
bar = dojo.coords(this.progressBar, true),
relMousePos = e[this._mousePixelCoord] - abspos[this._startingPixelCoord],
leftPos = bar[this._startingPixelCount],
rightPos = leftPos + bar[this._pixelCount],
isMaxVal = this._isReversed() ? relMousePos <= leftPos : relMousePos >= rightPos,
p = this._isReversed() ? abspos[this._pixelCount] - relMousePos : relMousePos
;
this._setPixelValue(p, abspos[this._pixelCount], true, isMaxVal);
dojo.stopEvent(e);
},
_setPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels, /*Boolean*/ priorityChange, /*Boolean*/ isMaxVal){
if(this.disabled || this.readOnly){ return; }
var myValue = this._getValueByPixelValue(pixelValue, maxPixels);
this._setValueAttr(myValue, priorityChange, isMaxVal);
},
_getValueByPixelValue: function(/*Number*/ pixelValue, /*Number*/ maxPixels){
pixelValue = pixelValue < 0 ? 0 : maxPixels < pixelValue ? maxPixels : pixelValue;
var count = this.discreteValues;
if(count <= 1 || count == Infinity){ count = maxPixels; }
count--;
var pixelsPerValue = maxPixels / count;
var wholeIncrements = Math.round(pixelValue / pixelsPerValue);
return (this.maximum-this.minimum)*wholeIncrements/count + this.minimum;
},
_setValueAttr: function(/*Array or Number*/ value, /*Boolean, optional*/ priorityChange, /*Boolean, optional*/ isMaxVal){
// we pass an array, when we move the slider with the bar
var actValue = this.value;
if(!dojo.isArray(value)){
if(isMaxVal){
if(this._isReversed()){
actValue[0] = value;
}else{
actValue[1] = value;
}
}else{
if(this._isReversed()){
actValue[1] = value;
}else{
actValue[0] = value;
}
}
}else{
actValue = value;
}
// we have to reset this values. don't know the reason for that
this._lastValueReported = "";
this.valueNode.value = this.value = value = actValue;
dijit.setWaiState(this.focusNode, "valuenow", actValue[0]);
dijit.setWaiState(this.focusNodeMax, "valuenow", actValue[1]);
this.value.sort(this._isReversed() ? sortReversed : sortForward);
// not calling the _setValueAttr-function of dijit.form.Slider, but the super-super-class (needed for the onchange-event!)
dijit.form._FormValueWidget.prototype._setValueAttr.apply(this, arguments);
this._printSliderBar(priorityChange, isMaxVal);
},
_printSliderBar: function(priorityChange, isMaxVal){
var percentMin = (this.value[0] - this.minimum) / (this.maximum - this.minimum);
var percentMax = (this.value[1] - this.minimum) / (this.maximum - this.minimum);
var percentMinSave = percentMin;
if(percentMin > percentMax){
percentMin = percentMax;
percentMax = percentMinSave;
}
var sliderHandleVal = this._isReversed() ? ((1-percentMin)*100) : (percentMin * 100);
var sliderHandleMaxVal = this._isReversed() ? ((1-percentMax)*100) : (percentMax * 100);
var progressBarVal = this._isReversed() ? ((1-percentMax)*100) : (percentMin * 100);
if (priorityChange && this.slideDuration > 0 && this.progressBar.style[this._progressPixelSize]){
// animate the slider
var percent = isMaxVal ? percentMax : percentMin;
var _this = this;
var props = {};
var start = parseFloat(this.progressBar.style[this._handleOffsetCoord]);
var duration = this.slideDuration / 10; // * (percent-start/100);
if(duration === 0){ return; }
if(duration < 0){ duration = 0 - duration; }
var propsHandle = {};
var propsHandleMax = {};
var propsBar = {};
// hui, a lot of animations :-)
propsHandle[this._handleOffsetCoord] = { start: this.sliderHandle.style[this._handleOffsetCoord], end: sliderHandleVal, units:"%"};
propsHandleMax[this._handleOffsetCoord] = { start: this.sliderHandleMax.style[this._handleOffsetCoord], end: sliderHandleMaxVal, units:"%"};
propsBar[this._handleOffsetCoord] = { start: this.progressBar.style[this._handleOffsetCoord], end: progressBarVal, units:"%"};
propsBar[this._progressPixelSize] = { start: this.progressBar.style[this._progressPixelSize], end: (percentMax - percentMin) * 100, units:"%"};
var animHandle = dojo.animateProperty({node: this.sliderHandle,duration: duration, properties: propsHandle});
var animHandleMax = dojo.animateProperty({node: this.sliderHandleMax,duration: duration, properties: propsHandleMax});
var animBar = dojo.animateProperty({node: this.progressBar,duration: duration, properties: propsBar});
var animCombine = dojo.fx.combine([animHandle, animHandleMax, animBar]);
animCombine.play();
}else{
this.sliderHandle.style[this._handleOffsetCoord] = sliderHandleVal + "%";
this.sliderHandleMax.style[this._handleOffsetCoord] = sliderHandleMaxVal + "%";
this.progressBar.style[this._handleOffsetCoord] = progressBarVal + "%";
this.progressBar.style[this._progressPixelSize] = ((percentMax - percentMin) * 100) + "%";
}
}
});
dojo.declare("dijit.form._SliderMoverMax", dijit.form._SliderMover, {
onMouseMove: function(e){
var widget = this.widget;
var abspos = widget._abspos;
if(!abspos){
abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
widget._isReversed_ = widget._isReversed();
}
var coordEvent = e.touches ? e.touches[0] : e; // if multitouch take first touch for coords
var pixelValue = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord];
widget._setPixelValue_(widget._isReversed_ ? (abspos[widget._pixelCount]-pixelValue) : pixelValue, abspos[widget._pixelCount], false, true);
},
destroy: function(e){
dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
var widget = this.widget;
widget._abspos = null;
widget._setValueAttr(widget.value, true);
}
});
dojo.declare("dijit.form._SliderBarMover", dojo.dnd.Mover, {
onMouseMove: function(e){
var widget = this.widget;
if(widget.disabled || widget.readOnly){ return; }
var abspos = widget._abspos;
var bar = widget._bar;
var mouseOffset = widget._mouseOffset;
if(!abspos){
abspos = widget._abspos = dojo.coords(widget.sliderBarContainer, true);
widget._setPixelValue_ = dojo.hitch(widget, "_setPixelValue");
widget._getValueByPixelValue_ = dojo.hitch(widget, "_getValueByPixelValue");
widget._isReversed_ = widget._isReversed();
}
if(!bar){
bar = widget._bar = dojo.coords(widget.progressBar, true);
}
var coordEvent = e.touches ? e.touches[0] : e; // if multitouch take first touch for coords
if(!mouseOffset){
mouseOffset = widget._mouseOffset = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - bar[widget._startingPixelCount];
}
var pixelValueMin = coordEvent[widget._mousePixelCoord] - abspos[widget._startingPixelCoord] - mouseOffset,
pixelValueMax = pixelValueMin + bar[widget._pixelCount];
// we don't narrow the slider when it reaches the bumper!
// maybe there is a simpler way
pixelValues = [pixelValueMin, pixelValueMax]
;
pixelValues.sort(sortForward);
if(pixelValues[0] <= 0){
pixelValues[0] = 0;
pixelValues[1] = bar[widget._pixelCount];
}
if(pixelValues[1] >= abspos[widget._pixelCount]){
pixelValues[1] = abspos[widget._pixelCount];
pixelValues[0] = abspos[widget._pixelCount] - bar[widget._pixelCount];
}
// getting the real values by pixel
var myValues = [
widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[0]) : pixelValues[0], abspos[widget._pixelCount]),
widget._getValueByPixelValue(widget._isReversed_ ? (abspos[widget._pixelCount] - pixelValues[1]) : pixelValues[1], abspos[widget._pixelCount])
];
// and setting the value of the widget
widget._setValueAttr(myValues, false, false);
},
destroy: function(){
dojo.dnd.Mover.prototype.destroy.apply(this, arguments);
var widget = this.widget;
widget._abspos = null;
widget._bar = null;
widget._mouseOffset = null;
widget._setValueAttr(widget.value, true);
}
});
dojo.declare("dojox.form.HorizontalRangeSlider",
[dijit.form.HorizontalSlider, dojox.form._RangeSliderMixin],
{
// summary:
// A form widget that allows one to select a range with two horizontally draggable images
templateString: dojo.cache("dojox.form", "resources/HorizontalRangeSlider.html", "\n")
}
);
dojo.declare("dojox.form.VerticalRangeSlider",
[dijit.form.VerticalSlider, dojox.form._RangeSliderMixin],
{
// summary:
// A form widget that allows one to select a range with two vertically draggable images
templateString: dojo.cache("dojox.form", "resources/VerticalRangeSlider.html", "\n")
}
);
})();
}
if(!dojo._hasResource["dojox.fx._core"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.fx._core"] = true;
dojo.provide("dojox.fx._core");
dojox.fx._Line = function(start, end){
// summary: a custom _Line to accomodate multi-dimensional values
//
// description:
// a normal dojo._Line is the curve, and does Line(start,end)
// for propertyAnimation. as we make more complicatied animations, we realize
// some properties can have 2, or 4 values relevant (x,y) or (t,l,r,b) for example
//
// this function provides support for those Lines, and is ported directly from 0.4
// this is a lot of extra code for something so seldom used, so we'll put it here as
// and optional core addition. you can create a new line, and use it during onAnimate
// as you see fit.
//
// start: Integer|Array
// An Integer (or an Array of integers) to use as a starting point
// end: Integer|Array
// An Integer (or an Array of integers) to use as an ending point
//
// example: see dojox.fx.smoothScroll
//
// example:
// | // this is 10 .. 100 and 50 .. 500
// | var curve = new dojox.fx._Line([10,50],[100,500]);
// | // dojo.Animation.onAnimate is called at every step of the animation
// | // to define current values. this _Line returns an array
// | // at each step. arguments[0] and [1] in this example.
//
this.start = start;
this.end = end;
var isArray = dojo.isArray(start),
d = (isArray ? [] : end - start);
if(isArray){
// multi-dimensional branch
dojo.forEach(this.start, function(s, i){
d[i] = this.end[i] - s;
}, this);
this.getValue = function(/*float*/ n){
var res = [];
dojo.forEach(this.start, function(s, i){
res[i] = (d[i] * n) + s;
}, this);
return res; // Array
}
}else{
// single value branch, document here for both branches:
this.getValue = function(/*float*/ n){
// summary: Returns the point on the line, or an array of points
// n: a floating point number greater than 0 and less than 1
// returns: Mixed
return (d * n) + this.start; // Decimal
}
}
};
}
if(!dojo._hasResource["dojox.json.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.json.query"] = true;
dojo.provide("dojox.json.query");
(function(){
dojox.json._slice = function(obj,start,end,step){
// handles slice operations: [3:6:2]
var len=obj.length,results = [];
end = end || len;
start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
for(var i=start; i, <=, >=, != - These operators behave just as they do
// in JavaScript.
//
//
//
// | dojox.json.query(queryString,object)
// and
// | dojox.json.query(queryString)(object)
// always return identical results. The first one immediately evaluates, the second one returns a
// function that then evaluates the object.
//
// example:
// | dojox.json.query("foo",{foo:"bar"})
// This will return "bar".
//
// example:
// | evaluator = dojox.json.query("?foo='bar'&rating>3");
// This creates a function that finds all the objects in an array with a property
// foo that is equals to "bar" and with a rating property with a value greater
// than 3.
// | evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
// This returns:
// | {foo:"bar",rating:4}
//
// example:
// | evaluator = dojox.json.query("$[?price<15.00][\rating][0:10]");
// This finds objects in array with a price less than 15.00 and sorts then
// by rating, highest rated first, and returns the first ten items in from this
// filtered and sorted list.
var depth = 0;
var str = [];
query = query.replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){
depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth
return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable
(t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings
t;
});
var prefix = '';
function call(name){
// creates a function call and puts the expression so far in a parameter for a call
prefix = name + "(" + prefix;
}
function makeRegex(t,a,b,c,d,e,f,g){
// creates a regular expression matcher for when wildcards and ignore case is used
return str[g].match(/[\*\?]/) || f == '~' ?
"/^" + str[g].substring(1,str[g].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,"[\\w\\W]$1") + (f == '~' ? '$/i' : '$/') + ".test(" + a + ")" :
t;
}
query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){
throw new Error("Unsafe function call");
});
query = query.replace(/([^=]=)([^=])/g,"$1=$2"). // change the equals to comparisons
replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){
return t.charAt(0) == '.' ? t : // leave .prop alone
t == '@' ? "$obj" :// the reference to the current object
(t.match(/:|^(\$|Math|true|false|null)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer
}).
replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){
var oper = t.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr
if(oper){
var prefix = '';
if(t.match(/^\./)){
// recursive object search
call("dojox.json._find");
prefix = ",true)";
}
call(oper[1].match(/\=/) ? "dojo.map" : oper[1].match(/\^/) ? "dojox.json._distinctFilter" : "dojo.filter");
return prefix + ",function($obj){return " + oper[2] + "})";
}
oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr]
if(oper){
// make a copy of the array and then sort it using the sorting expression
return ".concat().sort(function(a,b){" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){
return "var av= " + b.replace(/\$obj/,"a") + ",bv= " + b.replace(/\$obj/,"b") + // FIXME: Should check to make sure the $obj token isn't followed by characters
";if(av>bv||bv==null){return " + (a== "/" ? 1 : -1) +";}\n" +
"if(bv>av||av==null){return " + (a== "/" ? -1 : 1) +";}\n";
}) + "return 0;})";
}
oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3]
if(oper){
call("dojox.json._slice");
return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")";
}
if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*]
call("dojox.json._find");
return (t.charAt(1) == '.' ?
",'" + b + "'" : // ..prop
t.match(/,/) ?
"," + t : // [prop1,prop2]
"") + ")"; // [*]
}
return t;
}).
replace(/(\$obj\s*((\.\s*[\w_$]+\s*)|(\[\s*`([0-9]+)\s*`\]))*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching
replace(/`([0-9]+)\s*(==|~)\s*(\$obj\s*((\.\s*[\w_$]+)|(\[\s*`([0-9]+)\s*`\]))*)/g,function(t,a,b,c,d,e,f,g){ // and do it for reverse =
return makeRegex(t,c,d,e,f,g,b,a);
});
query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){
//restore the strings
return a == ']' ? ']' : str[a];
});
// create a function within this scope (so it can use expand and slice)
var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}");
for(var i = 0;i .myselector { filter:none; }
if(alphaImageLoader){
cssText = cssText.replace(alphaImageLoader, function(ignore, pre, delim, url, post){
return pre + (new dojo._Url(cssUrl, './'+url).toString()) + post;
});
}
return cssText.replace(cssPaths, function(ignore, delimStr, strUrl, delimUrl, urlUrl, media){
if(strUrl){
return '@import "' + (new dojo._Url(cssUrl, './'+strUrl).toString()) + '"' + media;
}else{
return 'url(' + (new dojo._Url(cssUrl, './'+urlUrl).toString()) + ')' + media;
}
});
};
// attributepaths one tag can have multiple paths, example:
// or
//
var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi;
var adjustHtmlPaths = dojox.html._adjustHtmlPaths = function(htmlUrl, cont){
var url = htmlUrl || "./";
return cont.replace(htmlAttrPaths,
function(tag, start, name, delim, relUrl, delim2, cssText, end){
return start + (name ?
(name + '=' + delim + (new dojo._Url(url, relUrl).toString()) + delim)
: ('style=' + delim2 + adjustCssPaths(url, cssText) + delim2)
) + end;
}
);
};
var snarfStyles = dojox.html._snarfStyles = function (/*String*/cssUrl, /*String*/cont, /*Array*/styles){
/**************** cut out all