This commit is contained in:
2015-06-05 10:07:46 +02:00
parent c09e26c103
commit 8fc57cc92e
13113 changed files with 1068470 additions and 864281 deletions
@@ -134,9 +134,9 @@ void DeviceRepoHandlerFactory::deleteDeviceEventQueue( const std::string& client
void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
if(req.find("Upgrade") != req.end() && Poco::icompare(req["Upgrade"], "websocket") == 0) {
//__handleEvent(req, resp, true);
throw webserver::webresource::WebResourceHandlerException("ToDo",
Net::HTTPResponse::HTTP_NOT_IMPLEMENTED);
__handleEvent(req, resp, true);
/*throw webserver::webresource::WebResourceHandlerException("ToDo",
Net::HTTPResponse::HTTP_NOT_IMPLEMENTED);*/
} else {
if (req.getMethod() == Net::HTTPRequest::HTTP_PUT) {
// Put Command
@@ -530,9 +530,7 @@ void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::N
DeviceEventQueue::DeviceEventQueue( const std::string& clientID,
DeviceRepositoryService* repoService,
DeviceRepoHandlerFactory* handlerFactory )
DeviceEventQueue::DeviceEventQueue( const std::string& clientID, DeviceRepositoryService* repoService, DeviceRepoHandlerFactory* handlerFactory )
: clientID(clientID), repoService(repoService), handlerFactory(handlerFactory), timer(handlerFactory->getIOService()) {
std::cout << "DeviceEventQueue \"" << clientID << "\" created" << std::endl;
}
@@ -0,0 +1,33 @@
html, body {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
}
#appLayout {
height: 100%;
}
#leftCol {
width: 14em;
}
.claro .demoLayout .edgePanel {
background-color: #d0e9fc;
}
#viewsChart {
width: 550px;
height: 550px;
}
.claro .dijitToggleButtonChecked .dijitButtonNode {
background-color: #0090ff;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset;
transition-duration: 0.1s;
}
@@ -0,0 +1,129 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Layout Draft 01</title>
<link rel="stylesheet" href="libs/dijit/themes/claro/claro.css" media="screen">
<!--<link rel="stylesheet" href="libs/dojox/widget/ColorPicker/ColorPicker.css" />-->
<link rel="stylesheet" href="draft01.css" media="screen">
<!-- configure Dojo -->
<script>
var dojoConfig = {
async: true,
parseOnLoad: true,
isDebug: true,
deps: [
"dojo/parser",
"dijit/layout/BorderContainer",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/Calendar",
"dijit/Fieldset",
"dijit/TitlePane",
"dijit/form/HorizontalSlider",
"dijit/form/HorizontalRule",
"dijit/form/HorizontalRuleLabels"
],
hos: {
deviceRepoUrl: "/hos/api/devices/"
}
};
</script>
<!-- load Dojo -->
<script src="libs/dojo/dojo.js"
data-dojo-config="async: true"></script>
</head>
<body class="claro">
<div id="appLayout" class="demoLayout" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'headline'">
<div class="centerPanel" data-dojo-type="dijit/layout/TabContainer" data-dojo-props="region: 'center', tabPosition: 'bottom'">
<div data-dojo-type="dijit/layout/ContentPane" title="Widgets">
<fieldset data-dojo-type="dijit/Fieldset">
<legend> Date/Time</legend>
<div data-dojo-type="dijit/Calendar" data-dojo-props="isDisabledDate:dojo.date.locale.isWeekend"></div>
</fieldset>
<div data-dojo-type="dijit/TitlePane" data-dojo-props="title: 'Widgets'">
<ul>
<li>
Toggle Button: <button data-dojo-type="dijit/form/ToggleButton" data-dojo-props="iconClass:'dijitCheckBoxIcon', checked: true">ON</button>
</li>
<li>
<div id="horizontalSlider"
style="width:400px;"
name="horizontalSlider"
data-dojo-type="dijit/form/HorizontalSlider"
data-dojo-props="value:6,
minimum: -10,
maximum:10,
discreteValues:11,
intermediateChanges:true,
showButtons:false">
<ol data-dojo-type="dijit/form/HorizontalRuleLabels" container="topDecoration"
style="height:1.5em;font-size:75%;color:gray;">
<li> </li>
<li>20%</li>
<li>40%</li>
<li>60%</li>
<li>80%</li>
<li> </li>
</ol>
<div data-dojo-type="dijit/form/HorizontalRule" container="bottomDecoration"
count=11 style="height:5px;"></div>
<ol data-dojo-type="dijit/form/HorizontalRuleLabels" container="bottomDecoration"
style="height:1em;font-size:75%;color:gray;">
<li>0%</li>
<li>50%</li>
<li>100%</li>
</ol>
</div>
</li>
</ul>
</div>
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Group 1">
<h4>Group 1 Content</h4>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Group Two">
<h4>Group 2 Content</h4>
</div>
</div>
<div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">
<b>Home Operating System:</b> bread crumbs or something similar
</div>
<div id="leftCol" class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'left', splitter: true">
<h4>Sitemap/Navigation</h4>
<ul>
<li>Overview</li>
<li>Living Room</li>
<li>Bed Room</li>
<li>Kitchen</li>
</ul>
</div>
</div>
</body>
</html>
@@ -0,0 +1,40 @@
html, body {
height: 100%;
margin: 0;
overflow: hidden;
padding: 0;
}
#appLayout {
height: 100%;
}
#leftCol {
width: 14em;
}
.claro .demoLayout .edgePanel {
background-color: #d0e9fc;
}
#viewsChart {
width: 550px;
height: 550px;
}
.claro .dijitToggleButtonChecked .dijitButtonNode {
background-color: #0090ff;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2) inset;
transition-duration: 0.1s;
}
.contentTable-valueCell {
float: right;
}
@@ -0,0 +1,243 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Layout Draft 02</title>
<link rel="stylesheet" href="libs/dijit/themes/claro/claro.css" media="screen">
<!--<link rel="stylesheet" href="libs/dojox/widget/ColorPicker/ColorPicker.css" />-->
<link rel="stylesheet" href="draft02.css" media="screen">
<!-- configure Dojo -->
<script>
var dojoConfig = {
async: true,
parseOnLoad: true,
isDebug: true,
deps: [
"dojo/parser",
"dijit/layout/BorderContainer",
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dijit/Calendar",
"dijit/Fieldset",
"dijit/TitlePane",
"dijit/form/HorizontalSlider",
"dijit/form/HorizontalRule",
"dijit/form/HorizontalRuleLabels",
"dijit/form/Button",
"dijit/form/Select",
"dijit/form/FilteringSelect",
"dijit/form/MultiSelect",
"dijit/form/HorizontalSlider",
"dijit/form/CheckBox",
"dijit/form/RadioButton",
"dijit/form/NumberSpinner",
"dijit/form/TextBox",
"dijit/form/Textarea"
],
hos: {
deviceRepoUrl: "/hos/api/devices/"
}
};
</script>
<!-- load Dojo -->
<script src="libs/dojo/dojo.js"
data-dojo-config="async: true"></script>
</head>
<body class="claro">
<div id="appLayout" class="demoLayout" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'headline'">
<div class="centerPanel" data-dojo-type="dijit/layout/TabContainer" data-dojo-props="region: 'center', tabPosition: 'bottom'">
<div data-dojo-type="dijit/layout/ContentPane" title="Form Widgets">
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>Button:</b>
<span style="float: right;"><button data-dojo-type="dijit/form/Button" type="button">Click me!</button></span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>ToggleButton:</b>
<span style="float: right;"><button data-dojo-type="dijit/form/ToggleButton" data-dojo-props="iconClass:'dijitCheckBoxIcon', checked: true">ON</button></span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>Select:</b>
<span style="float: right;">
<select data-dojo-type="dijit/form/Select">
<option value="TN">Tennessee</option>
<option value="VA" selected="selected">Virginia</option>
<option value="WA">Washington</option>
<option value="FL">Florida</option>
<option value="CA">California</option>
</select>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>FilteringSelect:</b>
<span style="float: right;">
<select data-dojo-type="dijit/form/FilteringSelect">
<option value="TN">Tennessee</option>
<option value="VA" selected="selected">Virginia</option>
<option value="WA">Washington</option>
<option value="FL">Florida</option>
<option value="CA">California</option>
</select>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>MultiSelect:</b>
<span style="float: right;">
<select data-dojo-type="dijit/form/MultiSelect">
<option value="TN">Tennessee</option>
<option value="VA" selected="selected">Virginia</option>
<option value="WA">Washington</option>
<option value="FL">Florida</option>
<option value="CA">California</option>
</select>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>HorizontalSlider:</b>
<span style="float: right; margin-right: 2px">
<div style="width:400px;"
data-dojo-type="dijit/form/HorizontalSlider"
data-dojo-props="value:0.5,
minimum: 0,
maximum:1,
intermediateChanges:true,
showButtons:true">
<ol data-dojo-type="dijit/form/HorizontalRuleLabels" container="topDecoration"
style="height:1.5em;font-size:75%;color:gray;">
<li> </li>
<li>20%</li>
<li>40%</li>
<li>60%</li>
<li>80%</li>
<li> </li>
</ol>
<div data-dojo-type="dijit/form/HorizontalRule" container="bottomDecoration" count="11" style="height:5px;"></div>
<ol data-dojo-type="dijit/form/HorizontalRuleLabels" container="bottomDecoration"
style="height:1em;font-size:75%;color:gray;">
<li>0%</li>
<li>50%</li>
<li>100%</li>
</ol>
</div>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>HorizontalSlider (discrete Values):</b>
<span style="float: right; margin-right: 2px">
<div style="width:400px;"
data-dojo-type="dijit/form/HorizontalSlider"
data-dojo-props="value:1,
discreteValues: 5,
minimum: 1,
maximum:5,
intermediateChanges:true,
showButtons:true">
<div data-dojo-type="dijit/form/HorizontalRule" container="bottomDecoration" count="5" style="height:5px;"></div>
<ol data-dojo-type="dijit/form/HorizontalRuleLabels" container="bottomDecoration"
style="height:1em;font-size:75%;color:gray;">
<li>cold</li>
<li>warm</li>
<li>hot</li>
</ol>
</div>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>CheckBox:</b>
<span style="float: right;">
<input id="mycheck" name="mycheck" data-dojo-type="dijit/form/CheckBox" value="agreed" checked/>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>RadioButton:</b>
<span style="float: right;">
<form>
<input type="radio" data-dojo-type="dijit/form/RadioButton" value="tea"/> <label>Tea</label>
<input type="radio" data-dojo-type="dijit/form/RadioButton" value="coffee"/> <label>Coffee</label>
<input type="radio" data-dojo-type="dijit/form/RadioButton" value="water"/> <label>Water</label>
</form>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>NumberSpinner:</b>
<span style="float: right;">
<input data-dojo-type="dijit/form/NumberSpinner"
value="1000"
data-dojo-props="smallDelta:10, constraints:{min:9,max:1550,places:0}"
style="width:100px"
/>
</span>
</div>
<div data-dojo-type="dijit/layout/ContentPane" style="border-style: solid; border-width: 1px">
<b>TextBox:</b>
<span style="float: right;">
<input type="text" name="firstname" value="testing testing"
data-dojo-type="dijit/form/TextBox"
data-dojo-props="trim:true, propercase:true" id="firstname" />
</span>
</div>
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Group 1">
<h4>Group 1 Content</h4>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
anim id est laborum.</p>
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Group Two">
<h4>Group 2 Content</h4>
</div>
</div>
<div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">
<b>Home Operating System:</b> bread crumbs or something similar
</div>
<div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'bottom'">
Footer
</div>
<div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'left', splitter: true">
<h4>Sitemap/Navigation</h4>
<ul>
<li>Overview</li>
<li>Living Room</li>
<li>Bed Room</li>
<li>Kitchen</li>
</ul>
</div>
<div class="edgePanel" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right'">
???
</div>
</div>
</body>
</html>
@@ -0,0 +1,40 @@
# Set the default behavior, in case users don't have core.autocrlf set
* text=auto
# Files that should always be normalized and converted to native line
# endings on checkout.
*.js text
*.json text
*.htm text
*.html text
*.svg text
*.txt text
*.xml text
*.xsl text
*.dtd text
*.css text
*.rest text
*.rst text
*.md text
*.php text
*.phps text
*.inc text
*.sh text
*.bat text
*.styl text
*.less text
Makefile text
README text
CHANGELOG text
LICENSE text
INSTALL text
BUILD text
# Files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.jar binary
*.zip binary
*.psd binary
@@ -1,63 +1,118 @@
//>>built
define("dijit/BackgroundIframe",["require","./main","dojo/_base/config","dojo/dom-construct","dojo/dom-style","dojo/_base/lang","dojo/on","dojo/sniff"],function(_1,_2,_3,_4,_5,_6,on,_7){
_7.add("config-bgIframe",(_7("ie")&&!/IEMobile\/10\.0/.test(navigator.userAgent))||(_7("trident")&&/Windows NT 6.[01]/.test(navigator.userAgent)));
var _8=new function(){
var _9=[];
this.pop=function(){
var _a;
if(_9.length){
_a=_9.pop();
_a.style.display="";
}else{
if(_7("ie")<9){
var _b=_3["dojoBlankHtmlUrl"]||_1.toUrl("dojo/resources/blank.html")||"javascript:\"\"";
var _c="<iframe src='"+_b+"' role='presentation'"+" style='position: absolute; left: 0px; top: 0px;"+"z-index: -1; filter:Alpha(Opacity=\"0\");'>";
_a=document.createElement(_c);
}else{
_a=_4.create("iframe");
_a.src="javascript:\"\"";
_a.className="dijitBackgroundIframe";
_a.setAttribute("role","presentation");
_5.set(_a,"opacity",0.1);
}
_a.tabIndex=-1;
}
return _a;
};
this.push=function(_d){
_d.style.display="none";
_9.push(_d);
};
}();
_2.BackgroundIframe=function(_e){
if(!_e.id){
throw new Error("no id");
}
if(_7("config-bgIframe")){
var _f=(this.iframe=_8.pop());
_e.appendChild(_f);
if(_7("ie")<7||_7("quirks")){
this.resize(_e);
this._conn=on(_e,"resize",_6.hitch(this,"resize",_e));
}else{
_5.set(_f,{width:"100%",height:"100%"});
}
}
};
_6.extend(_2.BackgroundIframe,{resize:function(_10){
if(this.iframe){
_5.set(this.iframe,{width:_10.offsetWidth+"px",height:_10.offsetHeight+"px"});
}
},destroy:function(){
if(this._conn){
this._conn.remove();
this._conn=null;
}
if(this.iframe){
this.iframe.parentNode.removeChild(this.iframe);
_8.push(this.iframe);
delete this.iframe;
}
}});
return _2.BackgroundIframe;
define([
"require", // require.toUrl
"./main", // to export dijit.BackgroundIframe
"dojo/_base/config",
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.set
"dojo/_base/lang", // lang.extend lang.hitch
"dojo/on",
"dojo/sniff" // has("ie"), has("trident"), has("quirks")
], function(require, dijit, config, domConstruct, domStyle, lang, on, has){
// module:
// dijit/BackgroundIFrame
// Flag for whether to create background iframe behind popups like Menus and Dialog.
// A background iframe is useful to prevent problems with popups appearing behind applets/pdf files,
// and is also useful on older versions of IE (IE6 and IE7) to prevent the "bleed through select" problem.
// By default, it's enabled for IE6-10, excluding Windows Phone 8,
// and it's also enabled for IE11 on Windows 7 and Windows 2008 Server.
// TODO: For 2.0, make this false by default. Also, possibly move definition to has.js so that this module can be
// conditionally required via dojo/has!bgIfame?dijit/BackgroundIframe
has.add("config-bgIframe",
(has("ie") && !/IEMobile\/10\.0/.test(navigator.userAgent)) || // No iframe on WP8, to match 1.9 behavior
(has("trident") && /Windows NT 6.[01]/.test(navigator.userAgent)));
var _frames = new function(){
// summary:
// cache of iframes
var queue = [];
this.pop = function(){
var iframe;
if(queue.length){
iframe = queue.pop();
iframe.style.display="";
}else{
// transparency needed for DialogUnderlay and for tooltips on IE (to see screen near connector)
if(has("ie") < 9){
var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
var html="<iframe src='" + burl + "' role='presentation'"
+ " style='position: absolute; left: 0px; top: 0px;"
+ "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
iframe = document.createElement(html);
}else{
iframe = domConstruct.create("iframe");
iframe.src = 'javascript:""';
iframe.className = "dijitBackgroundIframe";
iframe.setAttribute("role", "presentation");
domStyle.set(iframe, "opacity", 0.1);
}
iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
}
return iframe;
};
this.push = function(iframe){
iframe.style.display="none";
queue.push(iframe);
}
}();
dijit.BackgroundIframe = function(/*DomNode*/ node){
// summary:
// For IE/FF z-index shenanigans. id attribute is required.
//
// description:
// new dijit.BackgroundIframe(node).
//
// Makes a background iframe as a child of node, that fills
// area (and position) of node
if(!node.id){ throw new Error("no id"); }
if(has("config-bgIframe")){
var iframe = (this.iframe = _frames.pop());
node.appendChild(iframe);
if(has("ie")<7 || has("quirks")){
this.resize(node);
this._conn = on(node, 'resize', lang.hitch(this, "resize", node));
}else{
domStyle.set(iframe, {
width: '100%',
height: '100%'
});
}
}
};
lang.extend(dijit.BackgroundIframe, {
resize: function(node){
// summary:
// Resize the iframe so it's the same size as node.
// Needed on IE6 and IE/quirks because height:100% doesn't work right.
if(this.iframe){
domStyle.set(this.iframe, {
width: node.offsetWidth + 'px',
height: node.offsetHeight + 'px'
});
}
},
destroy: function(){
// summary:
// destroy the iframe
if(this._conn){
this._conn.remove();
this._conn = null;
}
if(this.iframe){
this.iframe.parentNode.removeChild(this.iframe);
_frames.push(this.iframe);
delete this.iframe;
}
}
});
return dijit.BackgroundIframe;
});
@@ -1,118 +0,0 @@
define("dijit/BackgroundIframe", [
"require", // require.toUrl
"./main", // to export dijit.BackgroundIframe
"dojo/_base/config",
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.set
"dojo/_base/lang", // lang.extend lang.hitch
"dojo/on",
"dojo/sniff" // has("ie"), has("trident"), has("quirks")
], function(require, dijit, config, domConstruct, domStyle, lang, on, has){
// module:
// dijit/BackgroundIFrame
// Flag for whether to create background iframe behind popups like Menus and Dialog.
// A background iframe is useful to prevent problems with popups appearing behind applets/pdf files,
// and is also useful on older versions of IE (IE6 and IE7) to prevent the "bleed through select" problem.
// By default, it's enabled for IE6-10, excluding Windows Phone 8,
// and it's also enabled for IE11 on Windows 7 and Windows 2008 Server.
// TODO: For 2.0, make this false by default. Also, possibly move definition to has.js so that this module can be
// conditionally required via dojo/has!bgIfame?dijit/BackgroundIframe
has.add("config-bgIframe",
(has("ie") && !/IEMobile\/10\.0/.test(navigator.userAgent)) || // No iframe on WP8, to match 1.9 behavior
(has("trident") && /Windows NT 6.[01]/.test(navigator.userAgent)));
var _frames = new function(){
// summary:
// cache of iframes
var queue = [];
this.pop = function(){
var iframe;
if(queue.length){
iframe = queue.pop();
iframe.style.display="";
}else{
// transparency needed for DialogUnderlay and for tooltips on IE (to see screen near connector)
if(has("ie") < 9){
var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
var html="<iframe src='" + burl + "' role='presentation'"
+ " style='position: absolute; left: 0px; top: 0px;"
+ "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
iframe = document.createElement(html);
}else{
iframe = domConstruct.create("iframe");
iframe.src = 'javascript:""';
iframe.className = "dijitBackgroundIframe";
iframe.setAttribute("role", "presentation");
domStyle.set(iframe, "opacity", 0.1);
}
iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
}
return iframe;
};
this.push = function(iframe){
iframe.style.display="none";
queue.push(iframe);
}
}();
dijit.BackgroundIframe = function(/*DomNode*/ node){
// summary:
// For IE/FF z-index shenanigans. id attribute is required.
//
// description:
// new dijit.BackgroundIframe(node).
//
// Makes a background iframe as a child of node, that fills
// area (and position) of node
if(!node.id){ throw new Error("no id"); }
if(has("config-bgIframe")){
var iframe = (this.iframe = _frames.pop());
node.appendChild(iframe);
if(has("ie")<7 || has("quirks")){
this.resize(node);
this._conn = on(node, 'resize', lang.hitch(this, "resize", node));
}else{
domStyle.set(iframe, {
width: '100%',
height: '100%'
});
}
}
};
lang.extend(dijit.BackgroundIframe, {
resize: function(node){
// summary:
// Resize the iframe so it's the same size as node.
// Needed on IE6 and IE/quirks because height:100% doesn't work right.
if(this.iframe){
domStyle.set(this.iframe, {
width: node.offsetWidth + 'px',
height: node.offsetHeight + 'px'
});
}
},
destroy: function(){
// summary:
// destroy the iframe
if(this._conn){
this._conn.remove();
this._conn = null;
}
if(this.iframe){
this.iframe.parentNode.removeChild(this.iframe);
_frames.push(this.iframe);
delete this.iframe;
}
}
});
return dijit.BackgroundIframe;
});
@@ -1,115 +1,323 @@
//>>built
define("dijit/Calendar",["dojo/_base/array","dojo/date","dojo/date/locale","dojo/_base/declare","dojo/dom-attr","dojo/dom-class","dojo/dom-construct","dojo/_base/kernel","dojo/keys","dojo/_base/lang","dojo/on","dojo/sniff","./CalendarLite","./_Widget","./_CssStateMixin","./_TemplatedMixin","./form/DropDownButton"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,on,_b,_c,_d,_e,_f,_10){
var _11=_4("dijit.Calendar",[_c,_d,_e],{baseClass:"dijitCalendar",cssStateNodes:{"decrementMonth":"dijitCalendarArrow","incrementMonth":"dijitCalendarArrow","previousYearLabelNode":"dijitCalendarPreviousYear","nextYearLabelNode":"dijitCalendarNextYear"},setValue:function(_12){
_8.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.","","2.0");
this.set("value",_12);
},_createMonthWidget:function(){
return new _11._MonthDropDownButton({id:this.id+"_mddb",tabIndex:-1,onMonthSelect:_a.hitch(this,"_onMonthSelect"),lang:this.lang,dateLocaleModule:this.dateLocaleModule},this.monthNode);
},postCreate:function(){
this.inherited(arguments);
this.own(on(this.domNode,"keydown",_a.hitch(this,"_onKeyDown")),on(this.dateRowsNode,"mouseover",_a.hitch(this,"_onDayMouseOver")),on(this.dateRowsNode,"mouseout",_a.hitch(this,"_onDayMouseOut")),on(this.dateRowsNode,"mousedown",_a.hitch(this,"_onDayMouseDown")),on(this.dateRowsNode,"mouseup",_a.hitch(this,"_onDayMouseUp")));
},_onMonthSelect:function(_13){
var _14=new this.dateClassObj(this.currentFocus);
_14.setDate(1);
_14.setMonth(_13);
var _15=this.dateModule.getDaysInMonth(_14);
var _16=this.currentFocus.getDate();
_14.setDate(Math.min(_16,_15));
this._setCurrentFocusAttr(_14);
},_onDayMouseOver:function(evt){
var _17=_6.contains(evt.target,"dijitCalendarDateLabel")?evt.target.parentNode:evt.target;
if(_17&&((_17.dijitDateValue&&!_6.contains(_17,"dijitCalendarDisabledDate"))||_17==this.previousYearLabelNode||_17==this.nextYearLabelNode)){
_6.add(_17,"dijitCalendarHoveredDate");
this._currentNode=_17;
}
},_onDayMouseOut:function(evt){
if(!this._currentNode){
return;
}
if(evt.relatedTarget&&evt.relatedTarget.parentNode==this._currentNode){
return;
}
var cls="dijitCalendarHoveredDate";
if(_6.contains(this._currentNode,"dijitCalendarActiveDate")){
cls+=" dijitCalendarActiveDate";
}
_6.remove(this._currentNode,cls);
this._currentNode=null;
},_onDayMouseDown:function(evt){
var _18=evt.target.parentNode;
if(_18&&_18.dijitDateValue&&!_6.contains(_18,"dijitCalendarDisabledDate")){
_6.add(_18,"dijitCalendarActiveDate");
this._currentNode=_18;
}
},_onDayMouseUp:function(evt){
var _19=evt.target.parentNode;
if(_19&&_19.dijitDateValue){
_6.remove(_19,"dijitCalendarActiveDate");
}
},handleKey:function(evt){
var _1a=-1,_1b,_1c=this.currentFocus;
switch(evt.keyCode){
case _9.RIGHT_ARROW:
_1a=1;
case _9.LEFT_ARROW:
_1b="day";
if(!this.isLeftToRight()){
_1a*=-1;
}
break;
case _9.DOWN_ARROW:
_1a=1;
case _9.UP_ARROW:
_1b="week";
break;
case _9.PAGE_DOWN:
_1a=1;
case _9.PAGE_UP:
_1b=evt.ctrlKey||evt.altKey?"year":"month";
break;
case _9.END:
_1c=this.dateModule.add(_1c,"month",1);
_1b="day";
case _9.HOME:
_1c=new this.dateClassObj(_1c);
_1c.setDate(1);
break;
default:
return true;
}
if(_1b){
_1c=this.dateModule.add(_1c,_1b,_1a);
}
this._setCurrentFocusAttr(_1c);
return false;
},_onKeyDown:function(evt){
if(!this.handleKey(evt)){
evt.stopPropagation();
evt.preventDefault();
}
},onValueSelected:function(){
},onChange:function(_1d){
this.onValueSelected(_1d);
},getClassForDate:function(){
}});
_11._MonthDropDownButton=_4("dijit.Calendar._MonthDropDownButton",_10,{onMonthSelect:function(){
},postCreate:function(){
this.inherited(arguments);
this.dropDown=new _11._MonthDropDown({id:this.id+"_mdd",onChange:this.onMonthSelect});
},_setMonthAttr:function(_1e){
var _1f=this.dateLocaleModule.getNames("months","wide","standAlone",this.lang,_1e);
this.dropDown.set("months",_1f);
this.containerNode.innerHTML=(_b("ie")==6?"":"<div class='dijitSpacer'>"+this.dropDown.domNode.innerHTML+"</div>")+"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>"+_1f[_1e.getMonth()]+"</div>";
}});
_11._MonthDropDown=_4("dijit.Calendar._MonthDropDown",[_d,_f,_e],{months:[],baseClass:"dijitCalendarMonthMenu dijitMenu",templateString:"<div data-dojo-attach-event='ondijitclick:_onClick'></div>",_setMonthsAttr:function(_20){
this.domNode.innerHTML="";
_1.forEach(_20,function(_21,idx){
var div=_7.create("div",{className:"dijitCalendarMonthLabel",month:idx,innerHTML:_21},this.domNode);
div._cssState="dijitCalendarMonthLabel";
},this);
},_onClick:function(evt){
this.onChange(_5.get(evt.target,"month"));
},onChange:function(){
}});
return _11;
define([
"dojo/_base/array", // array.map
"dojo/date",
"dojo/date/locale",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.get
"dojo/dom-class", // domClass.add domClass.contains domClass.remove domClass.toggle
"dojo/dom-construct", // create
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie")
"./CalendarLite",
"./_Widget",
"./_CssStateMixin",
"./_TemplatedMixin",
"./form/DropDownButton"
], function(array, date, local, declare, domAttr, domClass, domConstruct, kernel, keys, lang, on, has,
CalendarLite, _Widget, _CssStateMixin, _TemplatedMixin, DropDownButton){
// module:
// dijit/Calendar
// _Widget for deprecated methods like setAttribute()
var Calendar = declare("dijit.Calendar", [CalendarLite, _Widget, _CssStateMixin], {
// summary:
// A simple GUI for choosing a date in the context of a monthly calendar.
//
// description:
// See CalendarLite for general description. Calendar extends CalendarLite, adding:
//
// - month drop down list
// - keyboard navigation
// - CSS classes for hover/mousepress on date, month, and year nodes
// - support of deprecated methods (will be removed in 2.0)
baseClass: "dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details
cssStateNodes: {
"decrementMonth": "dijitCalendarArrow",
"incrementMonth": "dijitCalendarArrow",
"previousYearLabelNode": "dijitCalendarPreviousYear",
"nextYearLabelNode": "dijitCalendarNextYear"
},
setValue: function(/*Date*/ value){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
this.set('value', value);
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return new Calendar._MonthDropDownButton({
id: this.id + "_mddb",
tabIndex: -1,
onMonthSelect: lang.hitch(this, "_onMonthSelect"),
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
postCreate: function(){
this.inherited(arguments);
// Events specific to Calendar, not used in CalendarLite
this.own(
on(this.domNode, "keydown", lang.hitch(this, "_onKeyDown")),
on(this.dateRowsNode, "mouseover", lang.hitch(this, "_onDayMouseOver")),
on(this.dateRowsNode, "mouseout", lang.hitch(this, "_onDayMouseOut")),
on(this.dateRowsNode, "mousedown", lang.hitch(this, "_onDayMouseDown")),
on(this.dateRowsNode, "mouseup", lang.hitch(this, "_onDayMouseUp"))
);
},
_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: jan 31 --> feb 28, not feb 31)
var date = new this.dateClassObj(this.currentFocus);
date.setDate(1);
date.setMonth(newMonth);
var daysInMonth = this.dateModule.getDaysInMonth(date);
var currentDate = this.currentFocus.getDate();
date.setDate(Math.min(currentDate, daysInMonth));
this._setCurrentFocusAttr(date);
},
_onDayMouseOver: function(/*Event*/ evt){
// summary:
// Handler for mouse over events on days, sets hovered style
// tags:
// protected
// event can occur on <td> or the <span> inside the td,
// set node to the <td>.
var node =
domClass.contains(evt.target, "dijitCalendarDateLabel") ?
evt.target.parentNode :
evt.target;
if(node && (
(node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate"))
|| node == this.previousYearLabelNode || node == this.nextYearLabelNode
)){
domClass.add(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 <td> to <span> inside <td>, ignore it
if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){
return;
}
var cls = "dijitCalendarHoveredDate";
if(domClass.contains(this._currentNode, "dijitCalendarActiveDate")){
cls += " dijitCalendarActiveDate";
}
domClass.remove(this._currentNode, cls);
this._currentNode = null;
},
_onDayMouseDown: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate")){
domClass.add(node, "dijitCalendarActiveDate");
this._currentNode = node;
}
},
_onDayMouseUp: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue){
domClass.remove(node, "dijitCalendarActiveDate");
}
},
handleKey: function(/*Event*/ evt){
// summary:
// Provides keyboard navigation of calendar.
// description:
// Called from _onKeyDown() to handle keydown on a stand alone Calendar,
// and also from `dijit/form/_DateTimeTextBox` to pass a keydown 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 propagated
// tags:
// protected
var increment = -1,
interval,
newValue = this.currentFocus;
switch(evt.keyCode){
case keys.RIGHT_ARROW:
increment = 1;
//fallthrough...
case keys.LEFT_ARROW:
interval = "day";
if(!this.isLeftToRight()){
increment *= -1;
}
break;
case keys.DOWN_ARROW:
increment = 1;
//fallthrough...
case keys.UP_ARROW:
interval = "week";
break;
case keys.PAGE_DOWN:
increment = 1;
//fallthrough...
case keys.PAGE_UP:
interval = evt.ctrlKey || evt.altKey ? "year" : "month";
break;
case keys.END:
// go to the next month
newValue = this.dateModule.add(newValue, "month", 1);
// subtract a day from the result when we're done
interval = "day";
//fallthrough...
case keys.HOME:
newValue = new this.dateClassObj(newValue);
newValue.setDate(1);
break;
default:
return true;
}
if(interval){
newValue = this.dateModule.add(newValue, interval, increment);
}
this._setCurrentFocusAttr(newValue);
return false;
},
_onKeyDown: function(/*Event*/ evt){
// summary:
// For handling keydown events on a stand alone calendar
if(!this.handleKey(evt)){
evt.stopPropagation();
evt.preventDefault();
}
},
onValueSelected: function(/*Date*/ /*===== date =====*/){
// summary:
// Deprecated. 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(value){
this.onValueSelected(value); // remove in 2.0
},
getClassForDate: function(/*===== dateObject, 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.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
Calendar._MonthDropDownButton = declare("dijit.Calendar._MonthDropDownButton", DropDownButton, {
// summary:
// DropDownButton for the current month. Displays name of current month
// and a list of month names in the drop down
onMonthSelect: function(){
},
postCreate: function(){
this.inherited(arguments);
this.dropDown = new Calendar._MonthDropDown({
id: this.id + "_mdd", //do not change this id because it is referenced in the template
onChange: this.onMonthSelect
});
},
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
this.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 <TH> overlaps the right <TH> (due to a browser bug).
this.containerNode.innerHTML =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" + this.dropDown.domNode.innerHTML + "</div>") +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" + monthNames[month.getMonth()] + "</div>";
}
});
Calendar._MonthDropDown = declare("dijit.Calendar._MonthDropDown", [_Widget, _TemplatedMixin, _CssStateMixin], {
// summary:
// The list-of-months drop down from the MonthDropDownButton
// months: String[]
// List of names of months, possibly w/some undefined entries for Hebrew leap months
// (ex: ["January", "February", undefined, "April", ...])
months: [],
baseClass: "dijitCalendarMonthMenu dijitMenu",
templateString: "<div data-dojo-attach-event='ondijitclick:_onClick'></div>",
_setMonthsAttr: function(/*String[]*/ months){
this.domNode.innerHTML = "";
array.forEach(months, function(month, idx){
var div = domConstruct.create("div", {
className: "dijitCalendarMonthLabel",
month: idx,
innerHTML: month
}, this.domNode);
div._cssState = "dijitCalendarMonthLabel"; // trigger _CSSStateMixin magic; property, not attribute.
}, this);
},
_onClick: function(/*Event*/ evt){
this.onChange(domAttr.get(evt.target, "month"));
},
onChange: function(/*Number*/ /*===== month =====*/){
// summary:
// Callback when month is selected from drop down
}
});
return Calendar;
});
@@ -1,323 +0,0 @@
define("dijit/Calendar", [
"dojo/_base/array", // array.map
"dojo/date",
"dojo/date/locale",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.get
"dojo/dom-class", // domClass.add domClass.contains domClass.remove domClass.toggle
"dojo/dom-construct", // create
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie")
"./CalendarLite",
"./_Widget",
"./_CssStateMixin",
"./_TemplatedMixin",
"./form/DropDownButton"
], function(array, date, local, declare, domAttr, domClass, domConstruct, kernel, keys, lang, on, has,
CalendarLite, _Widget, _CssStateMixin, _TemplatedMixin, DropDownButton){
// module:
// dijit/Calendar
// _Widget for deprecated methods like setAttribute()
var Calendar = declare("dijit.Calendar", [CalendarLite, _Widget, _CssStateMixin], {
// summary:
// A simple GUI for choosing a date in the context of a monthly calendar.
//
// description:
// See CalendarLite for general description. Calendar extends CalendarLite, adding:
//
// - month drop down list
// - keyboard navigation
// - CSS classes for hover/mousepress on date, month, and year nodes
// - support of deprecated methods (will be removed in 2.0)
baseClass: "dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details
cssStateNodes: {
"decrementMonth": "dijitCalendarArrow",
"incrementMonth": "dijitCalendarArrow",
"previousYearLabelNode": "dijitCalendarPreviousYear",
"nextYearLabelNode": "dijitCalendarNextYear"
},
setValue: function(/*Date*/ value){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
this.set('value', value);
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return new Calendar._MonthDropDownButton({
id: this.id + "_mddb",
tabIndex: -1,
onMonthSelect: lang.hitch(this, "_onMonthSelect"),
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
postCreate: function(){
this.inherited(arguments);
// Events specific to Calendar, not used in CalendarLite
this.own(
on(this.domNode, "keydown", lang.hitch(this, "_onKeyDown")),
on(this.dateRowsNode, "mouseover", lang.hitch(this, "_onDayMouseOver")),
on(this.dateRowsNode, "mouseout", lang.hitch(this, "_onDayMouseOut")),
on(this.dateRowsNode, "mousedown", lang.hitch(this, "_onDayMouseDown")),
on(this.dateRowsNode, "mouseup", lang.hitch(this, "_onDayMouseUp"))
);
},
_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: jan 31 --> feb 28, not feb 31)
var date = new this.dateClassObj(this.currentFocus);
date.setDate(1);
date.setMonth(newMonth);
var daysInMonth = this.dateModule.getDaysInMonth(date);
var currentDate = this.currentFocus.getDate();
date.setDate(Math.min(currentDate, daysInMonth));
this._setCurrentFocusAttr(date);
},
_onDayMouseOver: function(/*Event*/ evt){
// summary:
// Handler for mouse over events on days, sets hovered style
// tags:
// protected
// event can occur on <td> or the <span> inside the td,
// set node to the <td>.
var node =
domClass.contains(evt.target, "dijitCalendarDateLabel") ?
evt.target.parentNode :
evt.target;
if(node && (
(node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate"))
|| node == this.previousYearLabelNode || node == this.nextYearLabelNode
)){
domClass.add(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 <td> to <span> inside <td>, ignore it
if(evt.relatedTarget && evt.relatedTarget.parentNode == this._currentNode){
return;
}
var cls = "dijitCalendarHoveredDate";
if(domClass.contains(this._currentNode, "dijitCalendarActiveDate")){
cls += " dijitCalendarActiveDate";
}
domClass.remove(this._currentNode, cls);
this._currentNode = null;
},
_onDayMouseDown: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue && !domClass.contains(node, "dijitCalendarDisabledDate")){
domClass.add(node, "dijitCalendarActiveDate");
this._currentNode = node;
}
},
_onDayMouseUp: function(/*Event*/ evt){
var node = evt.target.parentNode;
if(node && node.dijitDateValue){
domClass.remove(node, "dijitCalendarActiveDate");
}
},
handleKey: function(/*Event*/ evt){
// summary:
// Provides keyboard navigation of calendar.
// description:
// Called from _onKeyDown() to handle keydown on a stand alone Calendar,
// and also from `dijit/form/_DateTimeTextBox` to pass a keydown 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 propagated
// tags:
// protected
var increment = -1,
interval,
newValue = this.currentFocus;
switch(evt.keyCode){
case keys.RIGHT_ARROW:
increment = 1;
//fallthrough...
case keys.LEFT_ARROW:
interval = "day";
if(!this.isLeftToRight()){
increment *= -1;
}
break;
case keys.DOWN_ARROW:
increment = 1;
//fallthrough...
case keys.UP_ARROW:
interval = "week";
break;
case keys.PAGE_DOWN:
increment = 1;
//fallthrough...
case keys.PAGE_UP:
interval = evt.ctrlKey || evt.altKey ? "year" : "month";
break;
case keys.END:
// go to the next month
newValue = this.dateModule.add(newValue, "month", 1);
// subtract a day from the result when we're done
interval = "day";
//fallthrough...
case keys.HOME:
newValue = new this.dateClassObj(newValue);
newValue.setDate(1);
break;
default:
return true;
}
if(interval){
newValue = this.dateModule.add(newValue, interval, increment);
}
this._setCurrentFocusAttr(newValue);
return false;
},
_onKeyDown: function(/*Event*/ evt){
// summary:
// For handling keydown events on a stand alone calendar
if(!this.handleKey(evt)){
evt.stopPropagation();
evt.preventDefault();
}
},
onValueSelected: function(/*Date*/ /*===== date =====*/){
// summary:
// Deprecated. 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(value){
this.onValueSelected(value); // remove in 2.0
},
getClassForDate: function(/*===== dateObject, 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.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
Calendar._MonthDropDownButton = declare("dijit.Calendar._MonthDropDownButton", DropDownButton, {
// summary:
// DropDownButton for the current month. Displays name of current month
// and a list of month names in the drop down
onMonthSelect: function(){
},
postCreate: function(){
this.inherited(arguments);
this.dropDown = new Calendar._MonthDropDown({
id: this.id + "_mdd", //do not change this id because it is referenced in the template
onChange: this.onMonthSelect
});
},
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month);
this.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 <TH> overlaps the right <TH> (due to a browser bug).
this.containerNode.innerHTML =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" + this.dropDown.domNode.innerHTML + "</div>") +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" + monthNames[month.getMonth()] + "</div>";
}
});
Calendar._MonthDropDown = declare("dijit.Calendar._MonthDropDown", [_Widget, _TemplatedMixin, _CssStateMixin], {
// summary:
// The list-of-months drop down from the MonthDropDownButton
// months: String[]
// List of names of months, possibly w/some undefined entries for Hebrew leap months
// (ex: ["January", "February", undefined, "April", ...])
months: [],
baseClass: "dijitCalendarMonthMenu dijitMenu",
templateString: "<div data-dojo-attach-event='ondijitclick:_onClick'></div>",
_setMonthsAttr: function(/*String[]*/ months){
this.domNode.innerHTML = "";
array.forEach(months, function(month, idx){
var div = domConstruct.create("div", {
className: "dijitCalendarMonthLabel",
month: idx,
innerHTML: month
}, this.domNode);
div._cssState = "dijitCalendarMonthLabel"; // trigger _CSSStateMixin magic; property, not attribute.
}, this);
},
_onClick: function(/*Event*/ evt){
this.onChange(domAttr.get(evt.target, "month"));
},
onChange: function(/*Number*/ /*===== month =====*/){
// summary:
// Callback when month is selected from drop down
}
});
return Calendar;
});
@@ -1,198 +1,522 @@
//>>built
require({cache:{"url:dijit/templates/Calendar.html":"<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\" role=\"grid\" aria-labelledby=\"${id}_mddb ${id}_year\" data-dojo-attach-point=\"gridNode\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset dijitCalendarArrow' data-dojo-attach-point=\"decrementMonth\" scope=\"col\">\n\t\t\t\t<span class=\"dijitInline dijitCalendarIncrementControl dijitCalendarDecrease\" role=\"presentation\"></span>\n\t\t\t\t<span data-dojo-attach-point=\"decreaseArrowNode\" class=\"dijitA11ySideArrow\">-</span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\" scope=\"col\">\n\t\t\t\t<div data-dojo-attach-point=\"monthNode\">\n\t\t\t\t</div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset dijitCalendarArrow' scope=\"col\" data-dojo-attach-point=\"incrementMonth\">\n\t\t\t\t<span class=\"dijitInline dijitCalendarIncrementControl dijitCalendarIncrease\" role=\"presentation\"></span>\n\t\t\t\t<span data-dojo-attach-point=\"increaseArrowNode\" class=\"dijitA11ySideArrow\">+</span>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr role=\"row\">\n\t\t\t${!dayCellsHtml}\n\t\t</tr>\n\t</thead>\n\t<tbody data-dojo-attach-point=\"dateRowsNode\" data-dojo-attach-event=\"ondijitclick: _onDayClick\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t\t${!dateRowsHtml}\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\" role=\"presentation\">\n\t\t\t\t<div class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span data-dojo-attach-point=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\" role=\"button\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\" role=\"button\" id=\"${id}_year\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\" role=\"button\"></span>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\n"}});
define("dijit/CalendarLite",["dojo/_base/array","dojo/_base/declare","dojo/cldr/supplemental","dojo/date","dojo/date/locale","dojo/date/stamp","dojo/dom","dojo/dom-class","dojo/_base/lang","dojo/on","dojo/sniff","dojo/string","./_WidgetBase","./_TemplatedMixin","dojo/text!./templates/Calendar.html","./a11yclick","./hccss"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,on,_a,_b,_c,_d,_e){
var _f=_2("dijit.CalendarLite",[_c,_d],{templateString:_e,dowTemplateString:"<th class=\"dijitReset dijitCalendarDayLabelTemplate\" role=\"columnheader\" scope=\"col\"><span class=\"dijitCalendarDayLabel\">${d}</span></th>",dateTemplateString:"<td class=\"dijitReset\" role=\"gridcell\" data-dojo-attach-point=\"dateCells\"><span class=\"dijitCalendarDateLabel\" data-dojo-attach-point=\"dateLabels\"></span></td>",weekTemplateString:"<tr class=\"dijitReset dijitCalendarWeekTemplate\" role=\"row\">${d}${d}${d}${d}${d}${d}${d}</tr>",value:new Date(""),datePackage:"",dayWidth:"narrow",tabIndex:"0",currentFocus:new Date(),_setSummaryAttr:"gridNode",baseClass:"dijitCalendar dijitCalendarLite",_isValidDate:function(_10){
return _10&&!isNaN(_10)&&typeof _10=="object"&&_10.toString()!=this.constructor.prototype.value.toString();
},_getValueAttr:function(){
var _11=this._get("value");
if(_11&&!isNaN(_11)){
var _12=new this.dateClassObj(_11);
_12.setHours(0,0,0,0);
if(_12.getDate()<_11.getDate()){
_12=this.dateModule.add(_12,"hour",1);
}
return _12;
}else{
return null;
}
},_setValueAttr:function(_13,_14){
if(typeof _13=="string"){
_13=_6.fromISOString(_13);
}
_13=this._patchDate(_13);
if(this._isValidDate(_13)&&!this.isDisabledDate(_13,this.lang)){
this._set("value",_13);
this.set("currentFocus",_13);
this._markSelectedDates([_13]);
if(this._created&&(_14||typeof _14=="undefined")){
this.onChange(this.get("value"));
}
}else{
this._set("value",null);
this._markSelectedDates([]);
}
},_patchDate:function(_15){
if(_15){
_15=new this.dateClassObj(_15);
_15.setHours(1,0,0,0);
}
return _15;
},_setText:function(_16,_17){
while(_16.firstChild){
_16.removeChild(_16.firstChild);
}
_16.appendChild(_16.ownerDocument.createTextNode(_17));
},_populateGrid:function(){
var _18=new this.dateClassObj(this.currentFocus);
_18.setDate(1);
_18=this._patchDate(_18);
var _19=_18.getDay(),_1a=this.dateModule.getDaysInMonth(_18),_1b=this.dateModule.getDaysInMonth(this.dateModule.add(_18,"month",-1)),_1c=new this.dateClassObj(),_1d=_3.getFirstDayOfWeek(this.lang);
if(_1d>_19){
_1d-=7;
}
if(!this.summary){
var _1e=this.dateLocaleModule.getNames("months","wide","standAlone",this.lang,_18);
this.gridNode.setAttribute("summary",_1e[_18.getMonth()]);
}
this._date2cell={};
_1.forEach(this.dateCells,function(_1f,idx){
var i=idx+_1d;
var _20=new this.dateClassObj(_18),_21,_22="dijitCalendar",adj=0;
if(i<_19){
_21=_1b-_19+i+1;
adj=-1;
_22+="Previous";
}else{
if(i>=(_19+_1a)){
_21=i-_19-_1a+1;
adj=1;
_22+="Next";
}else{
_21=i-_19+1;
_22+="Current";
}
}
if(adj){
_20=this.dateModule.add(_20,"month",adj);
}
_20.setDate(_21);
if(!this.dateModule.compare(_20,_1c,"date")){
_22="dijitCalendarCurrentDate "+_22;
}
if(this.isDisabledDate(_20,this.lang)){
_22="dijitCalendarDisabledDate "+_22;
_1f.setAttribute("aria-disabled","true");
}else{
_22="dijitCalendarEnabledDate "+_22;
_1f.removeAttribute("aria-disabled");
_1f.setAttribute("aria-selected","false");
}
var _23=this.getClassForDate(_20,this.lang);
if(_23){
_22=_23+" "+_22;
}
_1f.className=_22+"Month dijitCalendarDateTemplate";
var _24=_20.valueOf();
this._date2cell[_24]=_1f;
_1f.dijitDateValue=_24;
this._setText(this.dateLabels[idx],_20.getDateLocalized?_20.getDateLocalized(this.lang):_20.getDate());
},this);
},_populateControls:function(){
var _25=new this.dateClassObj(this.currentFocus);
_25.setDate(1);
this.monthWidget.set("month",_25);
var y=_25.getFullYear()-1;
var d=new this.dateClassObj();
_1.forEach(["previous","current","next"],function(_26){
d.setFullYear(y++);
this._setText(this[_26+"YearLabelNode"],this.dateLocaleModule.format(d,{selector:"year",locale:this.lang}));
},this);
},goToToday:function(){
this.set("value",new this.dateClassObj());
},constructor:function(_27){
this.dateModule=_27.datePackage?_9.getObject(_27.datePackage,false):_4;
this.dateClassObj=this.dateModule.Date||Date;
this.dateLocaleModule=_27.datePackage?_9.getObject(_27.datePackage+".locale",false):_5;
},_createMonthWidget:function(){
return _f._MonthWidget({id:this.id+"_mddb",lang:this.lang,dateLocaleModule:this.dateLocaleModule},this.monthNode);
},buildRendering:function(){
var d=this.dowTemplateString,_28=this.dateLocaleModule.getNames("days",this.dayWidth,"standAlone",this.lang),_29=_3.getFirstDayOfWeek(this.lang);
this.dayCellsHtml=_b.substitute([d,d,d,d,d,d,d].join(""),{d:""},function(){
return _28[_29++%7];
});
var r=_b.substitute(this.weekTemplateString,{d:this.dateTemplateString});
this.dateRowsHtml=[r,r,r,r,r,r].join("");
this.dateCells=[];
this.dateLabels=[];
this.inherited(arguments);
_7.setSelectable(this.domNode,false);
var _2a=new this.dateClassObj(this.currentFocus);
this.monthWidget=this._createMonthWidget();
this.set("currentFocus",_2a,false);
},postCreate:function(){
this.inherited(arguments);
this._connectControls();
},_connectControls:function(){
var _2b=_9.hitch(this,function(_2c,_2d,_2e){
this[_2c].dojoClick=true;
return on(this[_2c],"click",_9.hitch(this,function(){
this._setCurrentFocusAttr(this.dateModule.add(this.currentFocus,_2d,_2e));
}));
});
this.own(_2b("incrementMonth","month",1),_2b("decrementMonth","month",-1),_2b("nextYearLabelNode","year",1),_2b("previousYearLabelNode","year",-1));
},_setCurrentFocusAttr:function(_2f,_30){
var _31=this.currentFocus,_32=this._getNodeByDate(_31);
_2f=this._patchDate(_2f);
this._set("currentFocus",_2f);
if(!this._date2cell||this.dateModule.difference(_31,_2f,"month")!=0){
this._populateGrid();
this._populateControls();
this._markSelectedDates([this.value]);
}
var _33=this._getNodeByDate(_2f);
_33.setAttribute("tabIndex",this.tabIndex);
if(this.focused||_30){
_33.focus();
}
if(_32&&_32!=_33){
if(_a("webkit")){
_32.setAttribute("tabIndex","-1");
}else{
_32.removeAttribute("tabIndex");
}
}
},focus:function(){
this._setCurrentFocusAttr(this.currentFocus,true);
},_onDayClick:function(evt){
evt.stopPropagation();
evt.preventDefault();
for(var _34=evt.target;_34&&!_34.dijitDateValue;_34=_34.parentNode){
}
if(_34&&!_8.contains(_34,"dijitCalendarDisabledDate")){
this.set("value",_34.dijitDateValue);
}
},_getNodeByDate:function(_35){
_35=this._patchDate(_35);
return _35&&this._date2cell?this._date2cell[_35.valueOf()]:null;
},_markSelectedDates:function(_36){
function _37(_38,_39){
_8.toggle(_39,"dijitCalendarSelectedDate",_38);
_39.setAttribute("aria-selected",_38?"true":"false");
};
_1.forEach(this._selectedCells||[],_9.partial(_37,false));
this._selectedCells=_1.filter(_1.map(_36,this._getNodeByDate,this),function(n){
return n;
});
_1.forEach(this._selectedCells,_9.partial(_37,true));
},onChange:function(){
},isDisabledDate:function(){
},getClassForDate:function(){
}});
_f._MonthWidget=_2("dijit.CalendarLite._MonthWidget",_c,{_setMonthAttr:function(_3a){
var _3b=this.dateLocaleModule.getNames("months","wide","standAlone",this.lang,_3a),_3c=(_a("ie")==6?"":"<div class='dijitSpacer'>"+_1.map(_3b,function(s){
return "<div>"+s+"</div>";
}).join("")+"</div>");
this.domNode.innerHTML=_3c+"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>"+_3b[_3a.getMonth()]+"</div>";
}});
return _f;
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/cldr/supplemental", // cldrSupplemental.getFirstDayOfWeek
"dojo/date", // date
"dojo/date/locale",
"dojo/date/stamp", // stamp.fromISOString
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.contains
"dojo/_base/lang", // lang.getObject, lang.hitch
"dojo/on",
"dojo/sniff", // has("ie") has("webkit")
"dojo/string", // string.substitute
"./_WidgetBase",
"./_TemplatedMixin",
"dojo/text!./templates/Calendar.html",
"./a11yclick", // not used directly, but template has ondijitclick in it
"./hccss" // not used directly, but sets CSS class on <body>
], function(array, declare, cldrSupplemental, date, locale, stamp, dom, domClass, lang, on, has, string, _WidgetBase, _TemplatedMixin, template){
// module:
// dijit/CalendarLite
var CalendarLite = declare("dijit.CalendarLite", [_WidgetBase, _TemplatedMixin], {
// summary:
// Lightweight version of Calendar widget aimed towards mobile use
//
// 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
// `<input>` 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.
//
// Also note that this widget isn't keyboard accessible; use dijit.Calendar for that
// example:
// | var calendar = new dijit.CalendarLite({}, dojo.byId("calendarNode"));
//
// example:
// | <div data-dojo-type="dijit/CalendarLite"></div>
// Template for main calendar
templateString: template,
// Template for cell for a day of the week (ex: M)
dowTemplateString: '<th class="dijitReset dijitCalendarDayLabelTemplate" role="columnheader" scope="col"><span class="dijitCalendarDayLabel">${d}</span></th>',
// Templates for a single date (ex: 13), and for a row for a week (ex: 20 21 22 23 24 25 26)
dateTemplateString: '<td class="dijitReset" role="gridcell" data-dojo-attach-point="dateCells"><span class="dijitCalendarDateLabel" data-dojo-attach-point="dateLabels"></span></td>',
weekTemplateString: '<tr class="dijitReset dijitCalendarWeekTemplate" role="row">${d}${d}${d}${d}${d}${d}${d}</tr>',
// 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. If unspecified, uses Gregorian calendar routines
// at dojo/date and dojo/date/locale.
datePackage: "",
// TODO: for 2.0, replace datePackage with dateModule and dateLocalModule attributes specifying MIDs,
// or alternately just get rid of this completely and tell user to use module ID remapping
// via require
// dayWidth: String
// How to represent the days of the week in the calendar header. See locale
dayWidth: "narrow",
// tabIndex: String
// 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(),
// Put the summary to the node with role=grid
_setSummaryAttr: "gridNode",
baseClass: "dijitCalendar dijitCalendarLite",
_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();
},
_getValueAttr: function(){
// summary:
// Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat
var storedVal = this._get("value");
if(storedVal && !isNaN(storedVal)){
var value = new this.dateClassObj(storedVal);
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() < storedVal.getDate()){
value = this.dateModule.add(value, "hour", 1);
}
return value;
}else{
return null;
}
},
_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(typeof value == "string"){
value = stamp.fromISOString(value);
}
value = this._patchDate(value);
if(this._isValidDate(value) && !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 to new month/year if necessary.
this.set("currentFocus", value);
// Mark the selected date
this._markSelectedDates([value]);
if(this._created && (priorityChange || typeof priorityChange == "undefined")){
this.onChange(this.get('value'));
}
}else{
// clear value, and mark all dates as unselected
this._set("value", null);
this._markSelectedDates([]);
}
},
_patchDate: function(/*Date|Number*/ value){
// summary:
// Convert Number into Date, or copy Date object. Then, round to nearest day,
// setting to 1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
if(value){
value = new this.dateClassObj(value);
value.setHours(1, 0, 0, 0);
}
return value;
},
_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(node.ownerDocument.createTextNode(text));
},
_populateGrid: function(){
// summary:
// Fills in the calendar grid with each day (1-31).
// Call this on creation, when moving to a new month.
// tags:
// private
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
month = this._patchDate(month); // needed if currentFocus is start or end of DST, see #17033
var firstDay = month.getDay(),
daysInMonth = this.dateModule.getDaysInMonth(month),
daysInPreviousMonth = this.dateModule.getDaysInMonth(this.dateModule.add(month, "month", -1)),
today = new this.dateClassObj(),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
if(dayOffset > firstDay){
dayOffset -= 7;
}
// If they didn't provide a summary, change the default summary to match with the new month
if(!this.summary){
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month)
this.gridNode.setAttribute("summary", monthNames[month.getMonth()]);
}
// Mapping from date (as specified by number returned from Date.valueOf()) to corresponding <td>
this._date2cell = {};
// Iterate through dates in the calendar and fill in date numbers and style info
array.forEach(this.dateCells, function(template, idx){
var i = idx + 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.dateModule.add(date, "month", adj);
}
date.setDate(number);
if(!this.dateModule.compare(date, today, "date")){
clazz = "dijitCalendarCurrentDate " + clazz;
}
if(this.isDisabledDate(date, this.lang)){
clazz = "dijitCalendarDisabledDate " + clazz;
template.setAttribute("aria-disabled", "true");
}else{
clazz = "dijitCalendarEnabledDate " + clazz;
template.removeAttribute("aria-disabled");
template.setAttribute("aria-selected", "false");
}
var clazz2 = this.getClassForDate(date, this.lang);
if(clazz2){
clazz = clazz2 + " " + clazz;
}
template.className = clazz + "Month dijitCalendarDateTemplate";
// Each cell has an associated integer value representing it's date
var dateVal = date.valueOf();
this._date2cell[dateVal] = template;
template.dijitDateValue = dateVal;
// Set Date string (ex: "13").
this._setText(this.dateLabels[idx], date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate());
}, this);
},
_populateControls: function(){
// summary:
// Fill in localized month, and prev/current/next years
// tags:
// protected
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
// set name of this month
this.monthWidget.set("month", month);
var y = month.getFullYear() - 1;
var d = new this.dateClassObj();
array.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(params /*===== , srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
this.dateModule = params.datePackage ? lang.getObject(params.datePackage, false) : date;
this.dateClassObj = this.dateModule.Date || Date;
this.dateLocaleModule = params.datePackage ? lang.getObject(params.datePackage + ".locale", false) : locale;
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return CalendarLite._MonthWidget({
id: this.id + "_mddb",
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
buildRendering: function(){
// Markup for days of the week (referenced from template)
var d = this.dowTemplateString,
dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
this.dayCellsHtml = string.substitute([d, d, d, d, d, d, d].join(""), {d: ""}, function(){
return dayNames[dayOffset++ % 7];
});
// Markup for dates of the month (referenced from template), but without numbers filled in
var r = string.substitute(this.weekTemplateString, {d: this.dateTemplateString});
this.dateRowsHtml = [r, r, r, r, r, r].join("");
// Instantiate from template.
// dateCells and dateLabels arrays filled when _Templated parses my template.
this.dateCells = [];
this.dateLabels = [];
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
var dateObj = new this.dateClassObj(this.currentFocus);
this.monthWidget = this._createMonthWidget();
this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
},
postCreate: function(){
this.inherited(arguments);
this._connectControls();
},
_connectControls: function(){
// summary:
// Set up connects for increment/decrement of months/years
// tags:
// protected
var connect = lang.hitch(this, function(nodeProp, part, amount){
this[nodeProp].dojoClick = true;
return on(this[nodeProp], "click", lang.hitch(this, function(){
this._setCurrentFocusAttr(this.dateModule.add(this.currentFocus, part, amount));
}));
});
this.own(
connect("incrementMonth", "month", 1),
connect("decrementMonth", "month", -1),
connect("nextYearLabelNode", "year", 1),
connect("previousYearLabelNode", "year", -1)
);
},
_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
// when Calendar is focused.
// forceFocus:
// If true, will focus() the cell even if calendar itself doesn't have focus
var oldFocus = this.currentFocus,
oldCell = this._getNodeByDate(oldFocus);
date = this._patchDate(date);
this._set("currentFocus", date);
// If the focus is on a different month than the current calendar month, switch the displayed month.
// Also will populate the grid initially, on Calendar creation.
if(!this._date2cell || this.dateModule.difference(oldFocus, date, "month") != 0){
this._populateGrid();
this._populateControls();
this._markSelectedDates([this.value]);
}
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
var newCell = this._getNodeByDate(date);
newCell.setAttribute("tabIndex", this.tabIndex);
if(this.focused || forceFocus){
newCell.focus();
}
// set tabIndex=-1 on old focusable cell
if(oldCell && oldCell != newCell){
if(has("webkit")){ // 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);
},
_onDayClick: function(/*Event*/ evt){
// summary:
// Handler for day clicks, selects the date if appropriate
// tags:
// protected
evt.stopPropagation();
evt.preventDefault();
for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode){
;
}
if(node && !domClass.contains(node, "dijitCalendarDisabledDate")){
this.set('value', node.dijitDateValue);
}
},
_getNodeByDate: function(/*Date*/ value){
// summary:
// Returns the cell corresponding to the date, or null if the date is not within the currently
// displayed month.
value = this._patchDate(value);
return value && this._date2cell ? this._date2cell[value.valueOf()] : null;
},
_markSelectedDates: function(/*Date[]*/ dates){
// summary:
// Marks the specified cells as selected, and clears cells previously marked as selected.
// For CalendarLite at most one cell is selected at any point, but this allows an array
// for easy subclassing.
// Function to mark a cell as selected or unselected
function mark(/*Boolean*/ selected, /*DomNode*/ cell){
domClass.toggle(cell, "dijitCalendarSelectedDate", selected);
cell.setAttribute("aria-selected", selected ? "true" : "false");
}
// Clear previously selected cells.
array.forEach(this._selectedCells || [], lang.partial(mark, false));
// Mark newly selected cells. Ignore dates outside the currently displayed month.
this._selectedCells = array.filter(array.map(dates, this._getNodeByDate, this), function(n){
return n;
});
array.forEach(this._selectedCells, lang.partial(mark, true));
},
onChange: function(/*Date*/ /*===== date =====*/){
// summary:
// Called only when the selected date has changed
},
isDisabledDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return false; // Boolean
=====*/
},
getClassForDate: function(/*===== dateObject, 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.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
CalendarLite._MonthWidget = declare("dijit.CalendarLite._MonthWidget", _WidgetBase, {
// summary:
// Displays name of current month padded to the width of the month
// w/the longest name, so that changing months doesn't change width.
//
// Create as:
// | new Calendar._MonthWidget({
// | lang: ...,
// | dateLocaleModule: ...
// | })
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month),
spacer =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" +
array.map(monthNames,function(s){
return "<div>" + s + "</div>";
}).join("") + "</div>");
// 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 <TH> overlaps the right <TH> (due to a browser bug).
this.domNode.innerHTML =
spacer +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" +
monthNames[month.getMonth()] + "</div>";
}
});
return CalendarLite;
});
@@ -1,524 +0,0 @@
require({cache:{
'url:dijit/templates/Calendar.html':"<table cellspacing=\"0\" cellpadding=\"0\" class=\"dijitCalendarContainer\" role=\"grid\" aria-labelledby=\"${id}_mddb ${id}_year\" data-dojo-attach-point=\"gridNode\">\n\t<thead>\n\t\t<tr class=\"dijitReset dijitCalendarMonthContainer\" valign=\"top\">\n\t\t\t<th class='dijitReset dijitCalendarArrow' data-dojo-attach-point=\"decrementMonth\" scope=\"col\">\n\t\t\t\t<span class=\"dijitInline dijitCalendarIncrementControl dijitCalendarDecrease\" role=\"presentation\"></span>\n\t\t\t\t<span data-dojo-attach-point=\"decreaseArrowNode\" class=\"dijitA11ySideArrow\">-</span>\n\t\t\t</th>\n\t\t\t<th class='dijitReset' colspan=\"5\" scope=\"col\">\n\t\t\t\t<div data-dojo-attach-point=\"monthNode\">\n\t\t\t\t</div>\n\t\t\t</th>\n\t\t\t<th class='dijitReset dijitCalendarArrow' scope=\"col\" data-dojo-attach-point=\"incrementMonth\">\n\t\t\t\t<span class=\"dijitInline dijitCalendarIncrementControl dijitCalendarIncrease\" role=\"presentation\"></span>\n\t\t\t\t<span data-dojo-attach-point=\"increaseArrowNode\" class=\"dijitA11ySideArrow\">+</span>\n\t\t\t</th>\n\t\t</tr>\n\t\t<tr role=\"row\">\n\t\t\t${!dayCellsHtml}\n\t\t</tr>\n\t</thead>\n\t<tbody data-dojo-attach-point=\"dateRowsNode\" data-dojo-attach-event=\"ondijitclick: _onDayClick\" class=\"dijitReset dijitCalendarBodyContainer\">\n\t\t\t${!dateRowsHtml}\n\t</tbody>\n\t<tfoot class=\"dijitReset dijitCalendarYearContainer\">\n\t\t<tr>\n\t\t\t<td class='dijitReset' valign=\"top\" colspan=\"7\" role=\"presentation\">\n\t\t\t\t<div class=\"dijitCalendarYearLabel\">\n\t\t\t\t\t<span data-dojo-attach-point=\"previousYearLabelNode\" class=\"dijitInline dijitCalendarPreviousYear\" role=\"button\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"currentYearLabelNode\" class=\"dijitInline dijitCalendarSelectedYear\" role=\"button\" id=\"${id}_year\"></span>\n\t\t\t\t\t<span data-dojo-attach-point=\"nextYearLabelNode\" class=\"dijitInline dijitCalendarNextYear\" role=\"button\"></span>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t</tfoot>\n</table>\n"}});
define("dijit/CalendarLite", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/cldr/supplemental", // cldrSupplemental.getFirstDayOfWeek
"dojo/date", // date
"dojo/date/locale",
"dojo/date/stamp", // stamp.fromISOString
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.contains
"dojo/_base/lang", // lang.getObject, lang.hitch
"dojo/on",
"dojo/sniff", // has("ie") has("webkit")
"dojo/string", // string.substitute
"./_WidgetBase",
"./_TemplatedMixin",
"dojo/text!./templates/Calendar.html",
"./a11yclick", // not used directly, but template has ondijitclick in it
"./hccss" // not used directly, but sets CSS class on <body>
], function(array, declare, cldrSupplemental, date, locale, stamp, dom, domClass, lang, on, has, string, _WidgetBase, _TemplatedMixin, template){
// module:
// dijit/CalendarLite
var CalendarLite = declare("dijit.CalendarLite", [_WidgetBase, _TemplatedMixin], {
// summary:
// Lightweight version of Calendar widget aimed towards mobile use
//
// 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
// `<input>` 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.
//
// Also note that this widget isn't keyboard accessible; use dijit.Calendar for that
// example:
// | var calendar = new dijit.CalendarLite({}, dojo.byId("calendarNode"));
//
// example:
// | <div data-dojo-type="dijit/CalendarLite"></div>
// Template for main calendar
templateString: template,
// Template for cell for a day of the week (ex: M)
dowTemplateString: '<th class="dijitReset dijitCalendarDayLabelTemplate" role="columnheader" scope="col"><span class="dijitCalendarDayLabel">${d}</span></th>',
// Templates for a single date (ex: 13), and for a row for a week (ex: 20 21 22 23 24 25 26)
dateTemplateString: '<td class="dijitReset" role="gridcell" data-dojo-attach-point="dateCells"><span class="dijitCalendarDateLabel" data-dojo-attach-point="dateLabels"></span></td>',
weekTemplateString: '<tr class="dijitReset dijitCalendarWeekTemplate" role="row">${d}${d}${d}${d}${d}${d}${d}</tr>',
// 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. If unspecified, uses Gregorian calendar routines
// at dojo/date and dojo/date/locale.
datePackage: "",
// TODO: for 2.0, replace datePackage with dateModule and dateLocalModule attributes specifying MIDs,
// or alternately just get rid of this completely and tell user to use module ID remapping
// via require
// dayWidth: String
// How to represent the days of the week in the calendar header. See locale
dayWidth: "narrow",
// tabIndex: String
// 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(),
// Put the summary to the node with role=grid
_setSummaryAttr: "gridNode",
baseClass: "dijitCalendar dijitCalendarLite",
_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();
},
_getValueAttr: function(){
// summary:
// Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat
var storedVal = this._get("value");
if(storedVal && !isNaN(storedVal)){
var value = new this.dateClassObj(storedVal);
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() < storedVal.getDate()){
value = this.dateModule.add(value, "hour", 1);
}
return value;
}else{
return null;
}
},
_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(typeof value == "string"){
value = stamp.fromISOString(value);
}
value = this._patchDate(value);
if(this._isValidDate(value) && !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 to new month/year if necessary.
this.set("currentFocus", value);
// Mark the selected date
this._markSelectedDates([value]);
if(this._created && (priorityChange || typeof priorityChange == "undefined")){
this.onChange(this.get('value'));
}
}else{
// clear value, and mark all dates as unselected
this._set("value", null);
this._markSelectedDates([]);
}
},
_patchDate: function(/*Date|Number*/ value){
// summary:
// Convert Number into Date, or copy Date object. Then, round to nearest day,
// setting to 1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
if(value){
value = new this.dateClassObj(value);
value.setHours(1, 0, 0, 0);
}
return value;
},
_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(node.ownerDocument.createTextNode(text));
},
_populateGrid: function(){
// summary:
// Fills in the calendar grid with each day (1-31).
// Call this on creation, when moving to a new month.
// tags:
// private
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
month = this._patchDate(month); // needed if currentFocus is start or end of DST, see #17033
var firstDay = month.getDay(),
daysInMonth = this.dateModule.getDaysInMonth(month),
daysInPreviousMonth = this.dateModule.getDaysInMonth(this.dateModule.add(month, "month", -1)),
today = new this.dateClassObj(),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
if(dayOffset > firstDay){
dayOffset -= 7;
}
// If they didn't provide a summary, change the default summary to match with the new month
if(!this.summary){
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month)
this.gridNode.setAttribute("summary", monthNames[month.getMonth()]);
}
// Mapping from date (as specified by number returned from Date.valueOf()) to corresponding <td>
this._date2cell = {};
// Iterate through dates in the calendar and fill in date numbers and style info
array.forEach(this.dateCells, function(template, idx){
var i = idx + 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.dateModule.add(date, "month", adj);
}
date.setDate(number);
if(!this.dateModule.compare(date, today, "date")){
clazz = "dijitCalendarCurrentDate " + clazz;
}
if(this.isDisabledDate(date, this.lang)){
clazz = "dijitCalendarDisabledDate " + clazz;
template.setAttribute("aria-disabled", "true");
}else{
clazz = "dijitCalendarEnabledDate " + clazz;
template.removeAttribute("aria-disabled");
template.setAttribute("aria-selected", "false");
}
var clazz2 = this.getClassForDate(date, this.lang);
if(clazz2){
clazz = clazz2 + " " + clazz;
}
template.className = clazz + "Month dijitCalendarDateTemplate";
// Each cell has an associated integer value representing it's date
var dateVal = date.valueOf();
this._date2cell[dateVal] = template;
template.dijitDateValue = dateVal;
// Set Date string (ex: "13").
this._setText(this.dateLabels[idx], date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate());
}, this);
},
_populateControls: function(){
// summary:
// Fill in localized month, and prev/current/next years
// tags:
// protected
var month = new this.dateClassObj(this.currentFocus);
month.setDate(1);
// set name of this month
this.monthWidget.set("month", month);
var y = month.getFullYear() - 1;
var d = new this.dateClassObj();
array.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(params /*===== , srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
this.dateModule = params.datePackage ? lang.getObject(params.datePackage, false) : date;
this.dateClassObj = this.dateModule.Date || Date;
this.dateLocaleModule = params.datePackage ? lang.getObject(params.datePackage + ".locale", false) : locale;
},
_createMonthWidget: function(){
// summary:
// Creates the drop down button that displays the current month and lets user pick a new one
return CalendarLite._MonthWidget({
id: this.id + "_mddb",
lang: this.lang,
dateLocaleModule: this.dateLocaleModule
}, this.monthNode);
},
buildRendering: function(){
// Markup for days of the week (referenced from template)
var d = this.dowTemplateString,
dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang),
dayOffset = cldrSupplemental.getFirstDayOfWeek(this.lang);
this.dayCellsHtml = string.substitute([d, d, d, d, d, d, d].join(""), {d: ""}, function(){
return dayNames[dayOffset++ % 7];
});
// Markup for dates of the month (referenced from template), but without numbers filled in
var r = string.substitute(this.weekTemplateString, {d: this.dateTemplateString});
this.dateRowsHtml = [r, r, r, r, r, r].join("");
// Instantiate from template.
// dateCells and dateLabels arrays filled when _Templated parses my template.
this.dateCells = [];
this.dateLabels = [];
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
var dateObj = new this.dateClassObj(this.currentFocus);
this.monthWidget = this._createMonthWidget();
this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
},
postCreate: function(){
this.inherited(arguments);
this._connectControls();
},
_connectControls: function(){
// summary:
// Set up connects for increment/decrement of months/years
// tags:
// protected
var connect = lang.hitch(this, function(nodeProp, part, amount){
this[nodeProp].dojoClick = true;
return on(this[nodeProp], "click", lang.hitch(this, function(){
this._setCurrentFocusAttr(this.dateModule.add(this.currentFocus, part, amount));
}));
});
this.own(
connect("incrementMonth", "month", 1),
connect("decrementMonth", "month", -1),
connect("nextYearLabelNode", "year", 1),
connect("previousYearLabelNode", "year", -1)
);
},
_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
// when Calendar is focused.
// forceFocus:
// If true, will focus() the cell even if calendar itself doesn't have focus
var oldFocus = this.currentFocus,
oldCell = this._getNodeByDate(oldFocus);
date = this._patchDate(date);
this._set("currentFocus", date);
// If the focus is on a different month than the current calendar month, switch the displayed month.
// Also will populate the grid initially, on Calendar creation.
if(!this._date2cell || this.dateModule.difference(oldFocus, date, "month") != 0){
this._populateGrid();
this._populateControls();
this._markSelectedDates([this.value]);
}
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused)
var newCell = this._getNodeByDate(date);
newCell.setAttribute("tabIndex", this.tabIndex);
if(this.focused || forceFocus){
newCell.focus();
}
// set tabIndex=-1 on old focusable cell
if(oldCell && oldCell != newCell){
if(has("webkit")){ // 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);
},
_onDayClick: function(/*Event*/ evt){
// summary:
// Handler for day clicks, selects the date if appropriate
// tags:
// protected
evt.stopPropagation();
evt.preventDefault();
for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode){
;
}
if(node && !domClass.contains(node, "dijitCalendarDisabledDate")){
this.set('value', node.dijitDateValue);
}
},
_getNodeByDate: function(/*Date*/ value){
// summary:
// Returns the cell corresponding to the date, or null if the date is not within the currently
// displayed month.
value = this._patchDate(value);
return value && this._date2cell ? this._date2cell[value.valueOf()] : null;
},
_markSelectedDates: function(/*Date[]*/ dates){
// summary:
// Marks the specified cells as selected, and clears cells previously marked as selected.
// For CalendarLite at most one cell is selected at any point, but this allows an array
// for easy subclassing.
// Function to mark a cell as selected or unselected
function mark(/*Boolean*/ selected, /*DomNode*/ cell){
domClass.toggle(cell, "dijitCalendarSelectedDate", selected);
cell.setAttribute("aria-selected", selected ? "true" : "false");
}
// Clear previously selected cells.
array.forEach(this._selectedCells || [], lang.partial(mark, false));
// Mark newly selected cells. Ignore dates outside the currently displayed month.
this._selectedCells = array.filter(array.map(dates, this._getNodeByDate, this), function(n){
return n;
});
array.forEach(this._selectedCells, lang.partial(mark, true));
},
onChange: function(/*Date*/ /*===== date =====*/){
// summary:
// Called only when the selected date has changed
},
isDisabledDate: function(/*===== dateObject, locale =====*/){
// summary:
// May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return false; // Boolean
=====*/
},
getClassForDate: function(/*===== dateObject, 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.
// dateObject: Date
// locale: String?
// tags:
// extension
/*=====
return ""; // String
=====*/
}
});
CalendarLite._MonthWidget = declare("dijit.CalendarLite._MonthWidget", _WidgetBase, {
// summary:
// Displays name of current month padded to the width of the month
// w/the longest name, so that changing months doesn't change width.
//
// Create as:
// | new Calendar._MonthWidget({
// | lang: ...,
// | dateLocaleModule: ...
// | })
_setMonthAttr: function(month){
// summary:
// Set the current month to display as a label
var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month),
spacer =
(has("ie") == 6 ? "" : "<div class='dijitSpacer'>" +
array.map(monthNames,function(s){
return "<div>" + s + "</div>";
}).join("") + "</div>");
// 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 <TH> overlaps the right <TH> (due to a browser bug).
this.domNode.innerHTML =
spacer +
"<div class='dijitCalendarMonthLabel dijitCalendarCurrentMonthLabel'>" +
monthNames[month.getMonth()] + "</div>";
}
});
return CalendarLite;
});
@@ -1,15 +1,56 @@
//>>built
require({cache:{"url:dijit/templates/CheckedMenuItem.html":"<tr class=\"dijitReset\" data-dojo-attach-point=\"focusNode\" role=\"${role}\" tabIndex=\"-1\" aria-checked=\"${checked}\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<span class=\"dijitInline dijitIcon dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"></span>\n\t\t<span class=\"dijitMenuItemIconChar dijitCheckedMenuItemIconChar\">${!checkedChar}</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode,textDirNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n"}});
define("dijit/CheckedMenuItem",["dojo/_base/declare","dojo/dom-class","./MenuItem","dojo/text!./templates/CheckedMenuItem.html","./hccss"],function(_1,_2,_3,_4){
return _1("dijit.CheckedMenuItem",_3,{baseClass:"dijitMenuItem dijitCheckedMenuItem",templateString:_4,checked:false,_setCheckedAttr:function(_5){
this.domNode.setAttribute("aria-checked",_5?"true":"false");
this._set("checked",_5);
},iconClass:"",role:"menuitemcheckbox",checkedChar:"&#10003;",onChange:function(){
},_onClick:function(_6){
if(!this.disabled){
this.set("checked",!this.checked);
this.onChange(this.checked);
}
this.onClick(_6);
}});
define([
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"./MenuItem",
"dojo/text!./templates/CheckedMenuItem.html",
"./hccss"
], function(declare, domClass, MenuItem, template){
// module:
// dijit/CheckedMenuItem
return declare("dijit.CheckedMenuItem", MenuItem, {
// summary:
// A checkbox-like menu item for toggling on and off
// Use both base classes so we get styles like dijitMenuItemDisabled
baseClass: "dijitMenuItem dijitCheckedMenuItem",
templateString: template,
// checked: Boolean
// Our checked state
checked: false,
_setCheckedAttr: function(/*Boolean*/ checked){
this.domNode.setAttribute("aria-checked", checked ? "true" : "false");
this._set("checked", checked); // triggers CSS update via _CssStateMixin
},
iconClass: "", // override dijitNoIcon
role: "menuitemcheckbox",
// checkedChar: String
// Character (or string) used in place of checkbox icon when display in high contrast mode
checkedChar: "&#10003;",
onChange: function(/*Boolean*/ /*===== checked =====*/){
// summary:
// User defined function to handle check/uncheck events
// tags:
// callback
},
_onClick: function(evt){
// summary:
// Clicking this item just toggles its state
// tags:
// private
if(!this.disabled){
this.set("checked", !this.checked);
this.onChange(this.checked);
}
this.onClick(evt);
}
});
});
@@ -1,58 +0,0 @@
require({cache:{
'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset\" data-dojo-attach-point=\"focusNode\" role=\"${role}\" tabIndex=\"-1\" aria-checked=\"${checked}\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<span class=\"dijitInline dijitIcon dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"></span>\n\t\t<span class=\"dijitMenuItemIconChar dijitCheckedMenuItemIconChar\">${!checkedChar}</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode,textDirNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n"}});
define("dijit/CheckedMenuItem", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"./MenuItem",
"dojo/text!./templates/CheckedMenuItem.html",
"./hccss"
], function(declare, domClass, MenuItem, template){
// module:
// dijit/CheckedMenuItem
return declare("dijit.CheckedMenuItem", MenuItem, {
// summary:
// A checkbox-like menu item for toggling on and off
// Use both base classes so we get styles like dijitMenuItemDisabled
baseClass: "dijitMenuItem dijitCheckedMenuItem",
templateString: template,
// checked: Boolean
// Our checked state
checked: false,
_setCheckedAttr: function(/*Boolean*/ checked){
this.domNode.setAttribute("aria-checked", checked ? "true" : "false");
this._set("checked", checked); // triggers CSS update via _CssStateMixin
},
iconClass: "", // override dijitNoIcon
role: "menuitemcheckbox",
// checkedChar: String
// Character (or string) used in place of checkbox icon when display in high contrast mode
checkedChar: "&#10003;",
onChange: function(/*Boolean*/ /*===== checked =====*/){
// summary:
// User defined function to handle check/uncheck events
// tags:
// callback
},
_onClick: function(evt){
// summary:
// Clicking this item just toggles its state
// tags:
// private
if(!this.disabled){
this.set("checked", !this.checked);
this.onChange(this.checked);
}
this.onClick(evt);
}
});
});
@@ -1,23 +1,160 @@
//>>built
require({cache:{"url:dijit/templates/ColorPalette.html":"<div class=\"dijitInline dijitColorPalette\" role=\"grid\">\n\t<table data-dojo-attach-point=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"presentation\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
define("dijit/ColorPalette",["require","dojo/text!./templates/ColorPalette.html","./_Widget","./_TemplatedMixin","./_PaletteMixin","./hccss","dojo/i18n","dojo/_base/Color","dojo/_base/declare","dojo/dom-construct","dojo/string","dojo/i18n!dojo/nls/colors","dojo/colors"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b){
var _c=_9("dijit.ColorPalette",[_3,_4,_5],{palette:"7x10",_palettes:{"7x10":[["white","seashell","cornsilk","lemonchiffon","lightyellow","palegreen","paleturquoise","lightcyan","lavender","plum"],["lightgray","pink","bisque","moccasin","khaki","lightgreen","lightseagreen","lightskyblue","cornflowerblue","violet"],["silver","lightcoral","sandybrown","orange","palegoldenrod","chartreuse","mediumturquoise","skyblue","mediumslateblue","orchid"],["gray","red","orangered","darkorange","yellow","limegreen","darkseagreen","royalblue","slateblue","mediumorchid"],["dimgray","crimson","chocolate","coral","gold","forestgreen","seagreen","blue","blueviolet","darkorchid"],["darkslategray","firebrick","saddlebrown","sienna","olive","green","darkcyan","mediumblue","darkslateblue","darkmagenta"],["black","darkred","maroon","brown","darkolivegreen","darkgreen","midnightblue","navy","indigo","purple"]],"3x4":[["white","lime","green","blue"],["silver","yellow","fuchsia","navy"],["gray","red","purple","black"]]},templateString:_2,baseClass:"dijitColorPalette",_dyeFactory:function(_d,_e,_f,_10){
return new this._dyeClass(_d,_e,_f,_10);
},buildRendering:function(){
this.inherited(arguments);
this._dyeClass=_9(_c._Color,{palette:this.palette});
this._preparePalette(this._palettes[this.palette],_7.getLocalization("dojo","colors",this.lang));
}});
_c._Color=_9("dijit._Color",_8,{template:"<span class='dijitInline dijitPaletteImg'>"+"<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>"+"</span>",hcTemplate:"<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>"+"<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>"+"</span>",_imagePaths:{"7x10":_1.toUrl("./themes/a11y/colors7x10.png"),"3x4":_1.toUrl("./themes/a11y/colors3x4.png")},constructor:function(_11,row,col,_12){
this._title=_12;
this._row=row;
this._col=col;
this.setColor(_8.named[_11]);
},getValue:function(){
return this.toHex();
},fillCell:function(_13,_14){
var _15=_b.substitute(_6("highcontrast")?this.hcTemplate:this.template,{color:this.toHex(),blankGif:_14,alt:this._title,title:this._title,image:this._imagePaths[this.palette].toString(),left:this._col*-20-5,top:this._row*-20-5,size:this.palette=="7x10"?"height: 145px; width: 206px":"height: 64px; width: 86px"});
_a.place(_15,_13);
}});
return _c;
define([
"require", // require.toUrl
"dojo/text!./templates/ColorPalette.html",
"./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
"./_TemplatedMixin",
"./_PaletteMixin",
"./hccss", // has("highcontrast")
"dojo/i18n", // i18n.getLocalization
"dojo/_base/Color", // dojo.Color dojo.Color.named
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/string", // string.substitute
"dojo/i18n!dojo/nls/colors", // translations
"dojo/colors" // extend dojo.Color w/names of other colors
], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, has, i18n, Color,
declare, domConstruct, string){
// module:
// dijit/ColorPalette
var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
// summary:
// A keyboard accessible color-picking widget
// description:
// Grid showing various colors, so the user can pick a certain color.
// Can be used standalone, or as a popup.
//
// example:
// | <div data-dojo-type="dijit/ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
// | picker.startup();
// palette: [const] String
// Size of grid, either "7x10" or "3x4".
palette: "7x10",
// _palettes: [protected] Map
// This represents the value of the colors.
// The first level is a hashmap of the different palettes available.
// The next two dimensions represent the columns and rows of colors.
_palettes: {
"7x10": [
["white", "seashell", "cornsilk", "lemonchiffon", "lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue", "orchid"],
["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
["darkslategray", "firebrick", "saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue", "darkslateblue", "darkmagenta" ],
["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]
],
"3x4": [
["white", "lime", "green", "blue"],
["silver", "yellow", "fuchsia", "navy"],
["gray", "red", "purple", "black"]
]
},
// templateString: String
// The template of this widget.
templateString: template,
baseClass: "dijitColorPalette",
_dyeFactory: function(value, row, col, title){
// Overrides _PaletteMixin._dyeFactory().
return new this._dyeClass(value, row, col, title);
},
buildRendering: function(){
// Instantiate the template, which makes a skeleton into which we'll insert a bunch of
// <img> nodes
this.inherited(arguments);
// Creates customized constructor for dye class (color of a single cell) for
// specified palette and high-contrast vs. normal mode. Used in _getDye().
this._dyeClass = declare(ColorPalette._Color, {
palette: this.palette
});
// Creates <img> nodes in each cell of the template.
this._preparePalette(
this._palettes[this.palette],
i18n.getLocalization("dojo", "colors", this.lang));
}
});
ColorPalette._Color = declare("dijit._Color", Color, {
// summary:
// Object associated with each cell in a ColorPalette palette.
// Implements dijit/Dye.
// Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
// node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
// for showing the color.
template: "<span class='dijitInline dijitPaletteImg'>" +
"<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
"</span>",
// Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
// but scrolled and clipped to show the correct color only
hcTemplate: "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
"<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
"</span>",
// _imagePaths: [protected] Map
// This is stores the path to the palette images used for high-contrast mode display
_imagePaths: {
"7x10": require.toUrl("./themes/a11y/colors7x10.png"),
"3x4": require.toUrl("./themes/a11y/colors3x4.png")
},
constructor: function(alias, row, col, title){
// summary:
// Constructor for ColorPalette._Color
// alias: String
// English name of the color.
// row: Number
// Vertical position in grid.
// column: Number
// Horizontal position in grid.
// title: String
// Localized name of the color.
this._title = title;
this._row = row;
this._col = col;
this.setColor(Color.named[alias]);
},
getValue: function(){
// summary:
// Note that although dijit._Color is initialized with a value like "white" getValue() always
// returns a hex value
return this.toHex();
},
fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
var html = string.substitute(has("highcontrast") ? this.hcTemplate : this.template, {
// substitution variables for normal mode
color: this.toHex(),
blankGif: blankGif,
alt: this._title,
title: this._title,
// variables used for high contrast mode
image: this._imagePaths[this.palette].toString(),
left: this._col * -20 - 5,
top: this._row * -20 - 5,
size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
});
domConstruct.place(html, cell);
}
});
return ColorPalette;
});
@@ -1,162 +0,0 @@
require({cache:{
'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\" role=\"grid\">\n\t<table data-dojo-attach-point=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"presentation\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
define("dijit/ColorPalette", [
"require", // require.toUrl
"dojo/text!./templates/ColorPalette.html",
"./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
"./_TemplatedMixin",
"./_PaletteMixin",
"./hccss", // has("highcontrast")
"dojo/i18n", // i18n.getLocalization
"dojo/_base/Color", // dojo.Color dojo.Color.named
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/string", // string.substitute
"dojo/i18n!dojo/nls/colors", // translations
"dojo/colors" // extend dojo.Color w/names of other colors
], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, has, i18n, Color,
declare, domConstruct, string){
// module:
// dijit/ColorPalette
var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
// summary:
// A keyboard accessible color-picking widget
// description:
// Grid showing various colors, so the user can pick a certain color.
// Can be used standalone, or as a popup.
//
// example:
// | <div data-dojo-type="dijit/ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
// | picker.startup();
// palette: [const] String
// Size of grid, either "7x10" or "3x4".
palette: "7x10",
// _palettes: [protected] Map
// This represents the value of the colors.
// The first level is a hashmap of the different palettes available.
// The next two dimensions represent the columns and rows of colors.
_palettes: {
"7x10": [
["white", "seashell", "cornsilk", "lemonchiffon", "lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue", "orchid"],
["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
["darkslategray", "firebrick", "saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue", "darkslateblue", "darkmagenta" ],
["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]
],
"3x4": [
["white", "lime", "green", "blue"],
["silver", "yellow", "fuchsia", "navy"],
["gray", "red", "purple", "black"]
]
},
// templateString: String
// The template of this widget.
templateString: template,
baseClass: "dijitColorPalette",
_dyeFactory: function(value, row, col, title){
// Overrides _PaletteMixin._dyeFactory().
return new this._dyeClass(value, row, col, title);
},
buildRendering: function(){
// Instantiate the template, which makes a skeleton into which we'll insert a bunch of
// <img> nodes
this.inherited(arguments);
// Creates customized constructor for dye class (color of a single cell) for
// specified palette and high-contrast vs. normal mode. Used in _getDye().
this._dyeClass = declare(ColorPalette._Color, {
palette: this.palette
});
// Creates <img> nodes in each cell of the template.
this._preparePalette(
this._palettes[this.palette],
i18n.getLocalization("dojo", "colors", this.lang));
}
});
ColorPalette._Color = declare("dijit._Color", Color, {
// summary:
// Object associated with each cell in a ColorPalette palette.
// Implements dijit/Dye.
// Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
// node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
// for showing the color.
template: "<span class='dijitInline dijitPaletteImg'>" +
"<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
"</span>",
// Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
// but scrolled and clipped to show the correct color only
hcTemplate: "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
"<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
"</span>",
// _imagePaths: [protected] Map
// This is stores the path to the palette images used for high-contrast mode display
_imagePaths: {
"7x10": require.toUrl("./themes/a11y/colors7x10.png"),
"3x4": require.toUrl("./themes/a11y/colors3x4.png")
},
constructor: function(alias, row, col, title){
// summary:
// Constructor for ColorPalette._Color
// alias: String
// English name of the color.
// row: Number
// Vertical position in grid.
// column: Number
// Horizontal position in grid.
// title: String
// Localized name of the color.
this._title = title;
this._row = row;
this._col = col;
this.setColor(Color.named[alias]);
},
getValue: function(){
// summary:
// Note that although dijit._Color is initialized with a value like "white" getValue() always
// returns a hex value
return this.toHex();
},
fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
var html = string.substitute(has("highcontrast") ? this.hcTemplate : this.template, {
// substitution variables for normal mode
color: this.toHex(),
blankGif: blankGif,
alt: this._title,
title: this._title,
// variables used for high contrast mode
image: this._imagePaths[this.palette].toString(),
left: this._col * -20 - 5,
top: this._row * -20 - 5,
size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
});
domConstruct.place(html, cell);
}
});
return ColorPalette;
});
@@ -1,4 +1,11 @@
//>>built
define("dijit/ConfirmDialog",["dojo/_base/declare","./Dialog","./_ConfirmDialogMixin"],function(_1,_2,_3){
return _1("dijit/ConfirmDialog",[_2,_3],{});
define([
"dojo/_base/declare",
"./Dialog",
"./_ConfirmDialogMixin"
], function(declare, Dialog, _ConfirmDialogMixin) {
return declare("dijit/ConfirmDialog", [Dialog, _ConfirmDialogMixin], {
// summary:
// A Dialog with OK/Cancel buttons.
});
});
@@ -1,11 +0,0 @@
define("dijit/ConfirmDialog", [
"dojo/_base/declare",
"./Dialog",
"./_ConfirmDialogMixin"
], function(declare, Dialog, _ConfirmDialogMixin) {
return declare("dijit/ConfirmDialog", [Dialog, _ConfirmDialogMixin], {
// summary:
// A Dialog with OK/Cancel buttons.
});
});
@@ -1,4 +1,11 @@
//>>built
define("dijit/ConfirmTooltipDialog",["dojo/_base/declare","./TooltipDialog","./_ConfirmDialogMixin"],function(_1,_2,_3){
return _1("dijit/ConfirmTooltipDialog",[_2,_3],{});
define([
"dojo/_base/declare",
"./TooltipDialog",
"./_ConfirmDialogMixin"
], function(declare, TooltipDialog, _ConfirmDialogMixin) {
return declare("dijit/ConfirmTooltipDialog", [TooltipDialog, _ConfirmDialogMixin], {
// summary:
// A TooltipDialog with OK/Cancel buttons.
});
});
@@ -1,11 +0,0 @@
define("dijit/ConfirmTooltipDialog", [
"dojo/_base/declare",
"./TooltipDialog",
"./_ConfirmDialogMixin"
], function(declare, TooltipDialog, _ConfirmDialogMixin) {
return declare("dijit/ConfirmTooltipDialog", [TooltipDialog, _ConfirmDialogMixin], {
// summary:
// A TooltipDialog with OK/Cancel buttons.
});
});
@@ -1,33 +1,115 @@
//>>built
define("dijit/Declaration",["dojo/_base/array","dojo/aspect","dojo/_base/declare","dojo/_base/lang","dojo/parser","dojo/query","./_Widget","./_TemplatedMixin","./_WidgetsInTemplateMixin","dojo/NodeList-dom"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9){
return _3("dijit.Declaration",_7,{_noScript:true,stopParser:true,widgetClass:"",defaults:null,mixins:[],buildRendering:function(){
var _a=this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),_b=_6("> script[type='dojo/method']",_a).orphan(),_c=_6("> script[type='dojo/connect']",_a).orphan(),_d=_6("> script[type='dojo/aspect']",_a).orphan(),_e=_a.nodeName;
var _f=this.defaults||{};
_1.forEach(_b,function(s){
var evt=s.getAttribute("event")||s.getAttribute("data-dojo-event"),_10=_5._functionFromScript(s,"data-dojo-");
if(evt){
_f[evt]=_10;
}else{
_d.push(s);
}
});
if(this.mixins.length){
this.mixins=_1.map(this.mixins,function(_11){
return _4.getObject(_11);
});
}else{
this.mixins=[_7,_8,_9];
}
_f._skipNodeCache=true;
_f.templateString="<"+_e+" class='"+_a.className+"'"+" data-dojo-attach-point='"+(_a.getAttribute("data-dojo-attach-point")||_a.getAttribute("dojoAttachPoint")||"")+"' data-dojo-attach-event='"+(_a.getAttribute("data-dojo-attach-event")||_a.getAttribute("dojoAttachEvent")||"")+"' >"+_a.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+_e+">";
var wc=_3(this.widgetClass,this.mixins,_f);
_1.forEach(_d,function(s){
var _12=s.getAttribute("data-dojo-advice")||"after",_13=s.getAttribute("data-dojo-method")||"postscript",_14=_5._functionFromScript(s);
_2.after(wc.prototype,_13,_14,true);
});
_1.forEach(_c,function(s){
var evt=s.getAttribute("event")||s.getAttribute("data-dojo-event"),_15=_5._functionFromScript(s);
_2.after(wc.prototype,evt,_15,true);
});
}});
define([
"dojo/_base/array", // array.forEach array.map
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"dojo/parser", // parser._functionFromScript
"dojo/query", // query
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"dojo/NodeList-dom"
], function(array, aspect, declare, lang, parser, query, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin){
// module:
// dijit/Declaration
return declare("dijit.Declaration", _Widget, {
// summary:
// The Declaration widget allows a developer to declare new widget
// classes directly from a snippet of markup.
// _noScript: [private] Boolean
// Flag to parser to leave alone the script tags contained inside of me
_noScript: true,
// stopParser: [private] Boolean
// Flag to parser to not try and parse widgets declared inside of me
stopParser: true,
// widgetClass: [const] String
// Name of class being declared, ex: "acme.myWidget"
widgetClass: "",
// propList: [const] Object
// Set of attributes for this widget along with default values, ex:
// {delay: 100, title: "hello world"}
defaults: null,
// mixins: [const] String[]
// List containing the prototype for this widget, and also any mixins,
// ex: ["dijit._Widget", "dijit._Container"]
mixins: [],
buildRendering: function(){
var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),
methods = query("> script[type='dojo/method']", src).orphan(),
connects = query("> script[type='dojo/connect']", src).orphan(), // remove for 2.0
aspects = query("> script[type='dojo/aspect']", src).orphan(),
srcType = src.nodeName;
var propList = this.defaults || {};
// For all methods defined like <script type="dojo/method" data-dojo-event="foo">,
// add that method to prototype.
// If there's no "event" specified then it's code to run on instantiation,
// so it becomes a connection to "postscript" (handled below).
array.forEach(methods, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"), // remove "event" for 2.0
func = parser._functionFromScript(s, "data-dojo-");
if(evt){
propList[evt] = func;
}else{
aspects.push(s);
}
});
// map array of strings like [ "dijit.form.Button" ] to array of mixin objects
// (note that array.map(this.mixins, lang.getObject) doesn't work because it passes
// a bogus third argument to getObject(), confusing it)
if(this.mixins.length){
this.mixins = array.map(this.mixins, function(name){ return lang.getObject(name); } );
}else{
this.mixins = [ _Widget, _TemplatedMixin, _WidgetsInTemplateMixin ];
}
propList._skipNodeCache = true;
propList.templateString =
"<"+srcType+" class='"+src.className+"'" +
" data-dojo-attach-point='"+
(src.getAttribute("data-dojo-attach-point") || src.getAttribute("dojoAttachPoint") || '')+
"' data-dojo-attach-event='"+
(src.getAttribute("data-dojo-attach-event") || src.getAttribute("dojoAttachEvent") || '')+
"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
// create the new widget class
var wc = declare(
this.widgetClass,
this.mixins,
propList
);
// Handle <script> blocks of form:
// <script type="dojo/aspect" data-dojo-advice="after" data-dojo-method="foo">
// and
// <script type="dojo/method">
// (Note that the second one is just shorthand for a dojo/aspect to postscript)
// Since this is a connect in the declaration, we are actually connection to the method
// in the _prototype_.
array.forEach(aspects, function(s){
var advice = s.getAttribute("data-dojo-advice") || "after",
method = s.getAttribute("data-dojo-method") || "postscript",
func = parser._functionFromScript(s);
aspect.after(wc.prototype, method, func, true);
});
// Handle legacy <script type="dojo/connect" data-dojo-event="foo">.
// Remove for 2.0.
array.forEach(connects, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"),
func = parser._functionFromScript(s);
aspect.after(wc.prototype, evt, func, true);
});
}
});
});
@@ -1,115 +0,0 @@
define("dijit/Declaration", [
"dojo/_base/array", // array.forEach array.map
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"dojo/parser", // parser._functionFromScript
"dojo/query", // query
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"dojo/NodeList-dom"
], function(array, aspect, declare, lang, parser, query, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin){
// module:
// dijit/Declaration
return declare("dijit.Declaration", _Widget, {
// summary:
// The Declaration widget allows a developer to declare new widget
// classes directly from a snippet of markup.
// _noScript: [private] Boolean
// Flag to parser to leave alone the script tags contained inside of me
_noScript: true,
// stopParser: [private] Boolean
// Flag to parser to not try and parse widgets declared inside of me
stopParser: true,
// widgetClass: [const] String
// Name of class being declared, ex: "acme.myWidget"
widgetClass: "",
// propList: [const] Object
// Set of attributes for this widget along with default values, ex:
// {delay: 100, title: "hello world"}
defaults: null,
// mixins: [const] String[]
// List containing the prototype for this widget, and also any mixins,
// ex: ["dijit._Widget", "dijit._Container"]
mixins: [],
buildRendering: function(){
var src = this.srcNodeRef.parentNode.removeChild(this.srcNodeRef),
methods = query("> script[type='dojo/method']", src).orphan(),
connects = query("> script[type='dojo/connect']", src).orphan(), // remove for 2.0
aspects = query("> script[type='dojo/aspect']", src).orphan(),
srcType = src.nodeName;
var propList = this.defaults || {};
// For all methods defined like <script type="dojo/method" data-dojo-event="foo">,
// add that method to prototype.
// If there's no "event" specified then it's code to run on instantiation,
// so it becomes a connection to "postscript" (handled below).
array.forEach(methods, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"), // remove "event" for 2.0
func = parser._functionFromScript(s, "data-dojo-");
if(evt){
propList[evt] = func;
}else{
aspects.push(s);
}
});
// map array of strings like [ "dijit.form.Button" ] to array of mixin objects
// (note that array.map(this.mixins, lang.getObject) doesn't work because it passes
// a bogus third argument to getObject(), confusing it)
if(this.mixins.length){
this.mixins = array.map(this.mixins, function(name){ return lang.getObject(name); } );
}else{
this.mixins = [ _Widget, _TemplatedMixin, _WidgetsInTemplateMixin ];
}
propList._skipNodeCache = true;
propList.templateString =
"<"+srcType+" class='"+src.className+"'" +
" data-dojo-attach-point='"+
(src.getAttribute("data-dojo-attach-point") || src.getAttribute("dojoAttachPoint") || '')+
"' data-dojo-attach-event='"+
(src.getAttribute("data-dojo-attach-event") || src.getAttribute("dojoAttachEvent") || '')+
"' >"+src.innerHTML.replace(/\%7B/g,"{").replace(/\%7D/g,"}")+"</"+srcType+">";
// create the new widget class
var wc = declare(
this.widgetClass,
this.mixins,
propList
);
// Handle <script> blocks of form:
// <script type="dojo/aspect" data-dojo-advice="after" data-dojo-method="foo">
// and
// <script type="dojo/method">
// (Note that the second one is just shorthand for a dojo/aspect to postscript)
// Since this is a connect in the declaration, we are actually connection to the method
// in the _prototype_.
array.forEach(aspects, function(s){
var advice = s.getAttribute("data-dojo-advice") || "after",
method = s.getAttribute("data-dojo-method") || "postscript",
func = parser._functionFromScript(s);
aspect.after(wc.prototype, method, func, true);
});
// Handle legacy <script type="dojo/connect" data-dojo-event="foo">.
// Remove for 2.0.
array.forEach(connects, function(s){
var evt = s.getAttribute("event") || s.getAttribute("data-dojo-event"),
func = parser._functionFromScript(s);
aspect.after(wc.prototype, evt, func, true);
});
}
});
});
@@ -1,35 +1,83 @@
//>>built
define("dijit/Destroyable",["dojo/_base/array","dojo/aspect","dojo/_base/declare"],function(_1,_2,_3){
return _3("dijit.Destroyable",null,{destroy:function(_4){
this._destroyed=true;
},own:function(){
var _5=["destroyRecursive","destroy","remove"];
_1.forEach(arguments,function(_6){
var _7;
var _8=_2.before(this,"destroy",function(_9){
_6[_7](_9);
});
var _a=[];
function _b(){
_8.remove();
_1.forEach(_a,function(_c){
_c.remove();
});
};
if(_6.then){
_7="cancel";
_6.then(_b,_b);
}else{
_1.forEach(_5,function(_d){
if(typeof _6[_d]==="function"){
if(!_7){
_7=_d;
}
_a.push(_2.after(_6,_d,_b,true));
}
});
}
},this);
return arguments;
}});
define([
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/declare"
], function(array, aspect, declare){
// module:
// dijit/Destroyable
return declare("dijit.Destroyable", null, {
// summary:
// Mixin to track handles and release them when instance is destroyed.
// description:
// Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
// dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
// Then call destroy() later to destroy this instance and release the resources.
destroy: function(/*Boolean*/ preserveDom){
// summary:
// Destroy this class, releasing any resources registered via own().
this._destroyed = true;
},
own: function(){
// summary:
// Track specified handles and remove/destroy them when this instance is destroyed, unless they were
// already removed/destroyed manually.
// tags:
// protected
// returns:
// The array of specified handles, so you can do for example:
// | var handle = this.own(on(...))[0];
var cleanupMethods = [
"destroyRecursive",
"destroy",
"remove"
];
array.forEach(arguments, function(handle){
// When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
// the handle will be destroyed before a subclass's destroy() method starts running, before it calls
// this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
// onDestroy() method and connect to that instead.
var destroyMethodName;
var odh = aspect.before(this, "destroy", function (preserveDom){
handle[destroyMethodName](preserveDom);
});
// Callback for when handle is manually destroyed.
var hdhs = [];
function onManualDestroy(){
odh.remove();
array.forEach(hdhs, function(hdh){
hdh.remove();
});
}
// Setup listeners for manual destroy of handle.
// Also computes destroyMethodName, used in listener above.
if(handle.then){
// Special path for Promises. Detect when Promise is resolved, rejected, or
// canceled (nb: cancelling a Promise causes it to be rejected).
destroyMethodName = "cancel";
handle.then(onManualDestroy, onManualDestroy);
}else{
// Path for other handles. Just use AOP to detect when handle is manually destroyed.
array.forEach(cleanupMethods, function(cleanupMethod){
if(typeof handle[cleanupMethod] === "function"){
if(!destroyMethodName){
// Use first matching method name in above listener (prefer destroyRecursive() to destroy())
destroyMethodName = cleanupMethod;
}
hdhs.push(aspect.after(handle, cleanupMethod, onManualDestroy, true));
}
});
}
}, this);
return arguments; // handle
}
});
});
@@ -1,83 +0,0 @@
define("dijit/Destroyable", [
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/declare"
], function(array, aspect, declare){
// module:
// dijit/Destroyable
return declare("dijit.Destroyable", null, {
// summary:
// Mixin to track handles and release them when instance is destroyed.
// description:
// Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
// dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
// Then call destroy() later to destroy this instance and release the resources.
destroy: function(/*Boolean*/ preserveDom){
// summary:
// Destroy this class, releasing any resources registered via own().
this._destroyed = true;
},
own: function(){
// summary:
// Track specified handles and remove/destroy them when this instance is destroyed, unless they were
// already removed/destroyed manually.
// tags:
// protected
// returns:
// The array of specified handles, so you can do for example:
// | var handle = this.own(on(...))[0];
var cleanupMethods = [
"destroyRecursive",
"destroy",
"remove"
];
array.forEach(arguments, function(handle){
// When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
// the handle will be destroyed before a subclass's destroy() method starts running, before it calls
// this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
// onDestroy() method and connect to that instead.
var destroyMethodName;
var odh = aspect.before(this, "destroy", function (preserveDom){
handle[destroyMethodName](preserveDom);
});
// Callback for when handle is manually destroyed.
var hdhs = [];
function onManualDestroy(){
odh.remove();
array.forEach(hdhs, function(hdh){
hdh.remove();
});
}
// Setup listeners for manual destroy of handle.
// Also computes destroyMethodName, used in listener above.
if(handle.then){
// Special path for Promises. Detect when Promise is resolved, rejected, or
// canceled (nb: cancelling a Promise causes it to be rejected).
destroyMethodName = "cancel";
handle.then(onManualDestroy, onManualDestroy);
}else{
// Path for other handles. Just use AOP to detect when handle is manually destroyed.
array.forEach(cleanupMethods, function(cleanupMethod){
if(typeof handle[cleanupMethod] === "function"){
if(!destroyMethodName){
// Use first matching method name in above listener (prefer destroyRecursive() to destroy())
destroyMethodName = cleanupMethod;
}
hdhs.push(aspect.after(handle, cleanupMethod, onManualDestroy, true));
}
});
}
}, this);
return arguments; // handle
}
});
});
File diff suppressed because it is too large Load Diff
@@ -1,698 +0,0 @@
require({cache:{
'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabindex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n\t${!actionBarTemplate}\n</div>\n\n"}});
define("dijit/Dialog", [
"require",
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/aspect",
"dojo/_base/declare", // declare
"dojo/Deferred", // Deferred
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.contains
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/i18n", // i18n.getLocalization
"dojo/keys",
"dojo/_base/lang", // lang.mixin lang.hitch
"dojo/on",
"dojo/ready",
"dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
"dojo/window", // winUtils.getBox, winUtils.get
"dojo/dnd/Moveable", // Moveable
"dojo/dnd/TimedMoveable", // TimedMoveable
"./focus",
"./_base/manager", // manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_CssStateMixin",
"./form/_FormMixin",
"./_DialogMixin",
"./DialogUnderlay",
"./layout/ContentPane",
"./layout/utils",
"dojo/text!./templates/Dialog.html",
"./a11yclick", // template uses ondijitclick
"dojo/i18n!./nls/common"
], function(require, array, aspect, declare, Deferred,
dom, domClass, domGeometry, domStyle, fx, i18n, keys, lang, on, ready, has, winUtils,
Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
DialogUnderlay, ContentPane, utils, template){
// module:
// dijit/Dialog
var resolvedDeferred = new Deferred();
resolvedDeferred.resolve(true);
var _DialogBase = declare("dijit._DialogBase" + (has("dojo-bidi") ? "_NoBidi" : ""), [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
templateString: template,
baseClass: "dijitDialog",
cssStateNodes: {
closeButtonNode: "dijitDialogCloseIcon"
},
// Map widget attributes to DOMNode attributes.
_setTitleAttr: { node: "titleNode", type: "innerHTML" },
// open: [readonly] Boolean
// True if Dialog is currently displayed on screen.
open: false,
// duration: Integer
// The time in milliseconds it takes the dialog to fade in and out
duration: manager.defaultDuration,
// refocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to re-focus the element which had focus before being opened.
// False will disable refocusing. Default: true
refocus: true,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true
autofocus: true,
// _firstFocusItem: [private readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_firstFocusItem: null,
// _lastFocusItem: [private readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_lastFocusItem: null,
// draggable: Boolean
// Toggles the movable aspect of the Dialog. If true, Dialog
// can be dragged by it's title. If false it will remain centered
// in the viewport.
draggable: true,
_setDraggableAttr: function(/*Boolean*/ val){
// Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
// as that prevents text select on modern browsers (#14452)
this._set("draggable", val);
},
// maxRatio: Number
// Maximum size to allow the dialog to expand to, relative to viewport size
maxRatio: 0.9,
// closable: Boolean
// Dialog show [x] icon to close itself, and ESC key will close the dialog.
closable: true,
_setClosableAttr: function(val){
this.closeButtonNode.style.display = val ? "" : "none";
this._set("closable", val);
},
postMixInProperties: function(){
var _nlsResources = i18n.getLocalization("dijit", "common");
lang.mixin(this, _nlsResources);
this.inherited(arguments);
},
postCreate: function(){
domStyle.set(this.domNode, {
display: "none",
position: "absolute"
});
this.ownerDocumentBody.appendChild(this.domNode);
this.inherited(arguments);
aspect.after(this, "onExecute", lang.hitch(this, "hide"), true);
aspect.after(this, "onCancel", lang.hitch(this, "hide"), true);
this._modalconnects = [];
},
onLoad: function(){
// summary:
// Called when data has been loaded from an href.
// Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
// but should *not* be overridden.
// tags:
// callback
// when href is specified we need to reposition the dialog after the data is loaded
// and find the focusable elements
this.resize();
this._position();
if(this.autofocus && DialogLevelManager.isTop(this)){
this._getFocusItems();
focus.focus(this._firstFocusItem);
}
this.inherited(arguments);
},
focus: function(){
this._getFocusItems();
focus.focus(this._firstFocusItem);
},
_endDrag: function(){
// summary:
// Called after dragging the Dialog. Saves the position of the dialog in the viewport,
// and also adjust position to be fully within the viewport, so user doesn't lose access to handle
var nodePosition = domGeometry.position(this.domNode),
viewport = winUtils.getBox(this.ownerDocument);
nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
this._relativePosition = nodePosition;
this._position();
},
_setup: function(){
// summary:
// Stuff we need to do before showing the Dialog for the first
// time (but we defer it until right beforehand, for
// performance reasons).
// tags:
// private
var node = this.domNode;
if(this.titleBar && this.draggable){
this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
: Moveable)(node, { handle: this.titleBar });
aspect.after(this._moveable, "onMoveStop", lang.hitch(this, "_endDrag"), true);
}else{
domClass.add(node, "dijitDialogFixed");
}
this.underlayAttrs = {
dialogId: this.id,
"class": array.map(this["class"].split(/\s/),function(s){
return s + "_underlay";
}).join(" "),
_onKeyDown: lang.hitch(this, "_onKey"),
ownerDocument: this.ownerDocument
};
},
_size: function(){
// TODO: remove for 2.0
this.resize();
},
_position: function(){
// summary:
// Position the dialog in the viewport. If no relative offset
// in the viewport has been determined (by dragging, for instance),
// center the dialog. Otherwise, use the Dialog's stored relative offset,
// adjusted by the viewport's scroll.
if(!domClass.contains(this.ownerDocumentBody, "dojoMove")){ // don't do anything if called during auto-scroll
var node = this.domNode,
viewport = winUtils.getBox(this.ownerDocument),
p = this._relativePosition,
bb = p ? null : domGeometry.position(node),
l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
;
domStyle.set(node, {
left: l + "px",
top: t + "px"
});
}
},
_onKey: function(/*Event*/ evt){
// summary:
// Handles the keyboard events for accessibility reasons
// tags:
// private
if(evt.keyCode == keys.TAB){
this._getFocusItems();
var node = evt.target;
if(this._firstFocusItem == this._lastFocusItem){
// don't move focus anywhere, but don't allow browser to move focus off of dialog either
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._firstFocusItem && evt.shiftKey){
// if we are shift-tabbing from first focusable item in dialog, send focus to last item
focus.focus(this._lastFocusItem);
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._lastFocusItem && !evt.shiftKey){
// if we are tabbing from last focusable item in dialog, send focus to first item
focus.focus(this._firstFocusItem);
evt.stopPropagation();
evt.preventDefault();
}
}else if(this.closable && evt.keyCode == keys.ESCAPE){
this.onCancel();
evt.stopPropagation();
evt.preventDefault();
}
},
show: function(){
// summary:
// Display the dialog
// returns: dojo/promise/Promise
// Promise object that resolves when the display animation is complete
if(this.open){
return resolvedDeferred.promise;
}
if(!this._started){
this.startup();
}
// first time we show the dialog, there's some initialization stuff to do
if(!this._alreadyInitialized){
this._setup();
this._alreadyInitialized = true;
}
if(this._fadeOutDeferred){
// There's a hide() operation in progress, so cancel it, but still call DialogLevelManager.hide()
// as though the hide() completed, in preparation for the DialogLevelManager.show() call below.
this._fadeOutDeferred.cancel();
DialogLevelManager.hide(this);
}
// Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
// Be sure that event object doesn't get passed to resize() method, because it's expecting an optional
// {w: ..., h:...} arg.
var win = winUtils.get(this.ownerDocument);
this._modalconnects.push(on(win, "scroll", lang.hitch(this, "resize", null)));
this._modalconnects.push(on(this.domNode, "keydown", lang.hitch(this, "_onKey")));
domStyle.set(this.domNode, {
opacity: 0,
display: ""
});
this._set("open", true);
this._onShow(); // lazy load trigger
this.resize();
this._position();
// fade-in Animation object, setup below
var fadeIn;
this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
fadeIn.stop();
delete this._fadeInDeferred;
}));
// If delay is 0, code below will delete this._fadeInDeferred instantly, so grab promise while we can.
var promise = this._fadeInDeferred.promise;
fadeIn = fx.fadeIn({
node: this.domNode,
duration: this.duration,
beforeBegin: lang.hitch(this, function(){
DialogLevelManager.show(this, this.underlayAttrs);
}),
onEnd: lang.hitch(this, function(){
if(this.autofocus && DialogLevelManager.isTop(this)){
// find focusable items each time dialog is shown since if dialog contains a widget the
// first focusable items can change
this._getFocusItems();
focus.focus(this._firstFocusItem);
}
this._fadeInDeferred.resolve(true);
delete this._fadeInDeferred;
})
}).play();
return promise;
},
hide: function(){
// summary:
// Hide the dialog
// returns: dojo/promise/Promise
// Promise object that resolves when the display animation is complete
// If we haven't been initialized yet then we aren't showing and we can just return.
// Likewise if we are already hidden, or are currently fading out.
if(!this._alreadyInitialized || !this.open){
return resolvedDeferred.promise;
}
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
// fade-in Animation object, setup below
var fadeOut;
this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
fadeOut.stop();
delete this._fadeOutDeferred;
}));
// fire onHide when the promise resolves.
this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
// If delay is 0, code below will delete this._fadeOutDeferred instantly, so grab promise while we can.
var promise = this._fadeOutDeferred.promise;
fadeOut = fx.fadeOut({
node: this.domNode,
duration: this.duration,
onEnd: lang.hitch(this, function(){
this.domNode.style.display = "none";
DialogLevelManager.hide(this);
this._fadeOutDeferred.resolve(true);
delete this._fadeOutDeferred;
})
}).play();
if(this._scrollConnected){
this._scrollConnected = false;
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
if(this._relativePosition){
delete this._relativePosition;
}
this._set("open", false);
return promise;
},
resize: function(dim){
// summary:
// Called with no argument when viewport scrolled or viewport size changed. Adjusts Dialog as
// necessary to keep it visible.
//
// Can also be called with an argument (by dojox/layout/ResizeHandle etc.) to explicitly set the
// size of the dialog.
// dim: Object?
// Optional dimension object like {w: 200, h: 300}
if(this.domNode.style.display != "none"){
this._checkIfSingleChild();
if(!dim){
if(this._shrunk){
// If we earlier shrunk the dialog to fit in the viewport, reset it to its natural size
if(this._singleChild){
if(typeof this._singleChildOriginalStyle != "undefined"){
this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
delete this._singleChildOriginalStyle;
}
}
array.forEach([this.domNode, this.containerNode, this.titleBar, this.actionBarNode], function(node){
if(node){ // because titleBar may not be defined
domStyle.set(node, {
position: "static",
width: "auto",
height: "auto"
});
}
});
this.domNode.style.position = "absolute";
}
// If necessary, shrink Dialog to fit in viewport and have some space around it
// to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
var viewport = winUtils.getBox(this.ownerDocument);
viewport.w *= this.maxRatio;
viewport.h *= this.maxRatio;
var bb = domGeometry.position(this.domNode);
if(bb.w >= viewport.w || bb.h >= viewport.h){
dim = {
w: Math.min(bb.w, viewport.w),
h: Math.min(bb.h, viewport.h)
};
this._shrunk = true;
}else{
this._shrunk = false;
}
}
// Code to run if user has requested an explicit size, or the shrinking code above set an implicit size
if(dim){
// Set this.domNode to specified size
domGeometry.setMarginBox(this.domNode, dim);
// And then size this.containerNode
var layoutNodes = [];
if(this.titleBar){
layoutNodes.push({domNode: this.titleBar, region: "top"});
}
if(this.actionBarNode){
layoutNodes.push({domNode: this.actionBarNode, region: "bottom"});
}
var centerSize = {domNode: this.containerNode, region: "center"};
layoutNodes.push(centerSize);
var contentDim = utils.marginBox2contentBox(this.domNode, dim);
utils.layoutChildren(this.domNode, contentDim, layoutNodes);
// And then if this.containerNode has a single layout widget child, size it too.
// Otherwise, make this.containerNode show a scrollbar if it's overflowing.
if(this._singleChild){
var cb = utils.marginBox2contentBox(this.containerNode, centerSize);
// note: if containerNode has padding singleChildSize will have l and t set,
// but don't pass them to resize() or it will doubly-offset the child
this._singleChild.resize({w: cb.w, h: cb.h});
// TODO: save original size for restoring it on another show()?
}else{
this.containerNode.style.overflow = "auto";
this._layoutChildren(); // send resize() event to all child widgets
}
}else{
this._layoutChildren(); // send resize() event to all child widgets
}
if(!has("touch") && !dim){
// If the user has scrolled the viewport then reposition the Dialog. But don't do it for touch
// devices, because it will counteract when a keyboard pops up and then the browser auto-scrolls
// the focused node into view.
this._position();
}
}
},
_layoutChildren: function(){
// Override _ContentPaneResizeMixin._layoutChildren because even when there's just a single layout child
// widget, sometimes we don't want to size it explicitly (i.e. to pass a dim argument to resize())
array.forEach(this.getChildren(), function(widget){
if(widget.resize){
widget.resize();
}
});
},
destroy: function(){
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
if(this._fadeOutDeferred){
this._fadeOutDeferred.cancel();
}
if(this._moveable){
this._moveable.destroy();
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
DialogLevelManager.hide(this);
this.inherited(arguments);
}
});
if(has("dojo-bidi")){
_DialogBase = declare("dijit._DialogBase", _DialogBase, {
_setTitleAttr: function(/*String*/ title){
this._set("title", title);
this.titleNode.innerHTML = title;
this.applyTextDir(this.titleNode);
},
_setTextDirAttr: function(textDir){
if(this._created && this.textDir != textDir){
this._set("textDir", textDir);
this.set("title", this.title);
}
}
});
}
var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {
// summary:
// A modal dialog Widget.
// description:
// Pops up a modal dialog window, blocking access to the screen
// and also graying out the screen Dialog is extended from
// ContentPane so it supports all the same parameters (href, etc.).
// example:
// | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
// example:
// | var foo = new Dialog({ title: "test dialog", content: "test content" });
// | foo.placeAt(win.body());
// | foo.startup();
});
Dialog._DialogBase = _DialogBase; // for monkey patching and dojox/widget/DialogSimple
var DialogLevelManager = Dialog._DialogLevelManager = {
// summary:
// Controls the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
_beginZIndex: 950,
show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs){
// summary:
// Call right before fade-in animation for new dialog.
// Saves current focus, displays/adjusts underlay for new dialog,
// and sets the z-index of the dialog itself.
//
// New dialog will be displayed on top of all currently displayed dialogs.
//
// Caller is responsible for setting focus in new dialog after the fade-in
// animation completes.
// Save current focus
ds[ds.length - 1].focus = focus.curNode;
// Set z-index a bit above previous dialog
var zIndex = ds[ds.length - 1].dialog ? ds[ds.length - 1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
domStyle.set(dialog.domNode, 'zIndex', zIndex);
// Display the underlay, or if already displayed then adjust for this new dialog
DialogUnderlay.show(underlayAttrs, zIndex - 1);
ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
},
hide: function(/*dijit/_WidgetBase*/ dialog){
// summary:
// Called when the specified dialog is hidden/destroyed, after the fade-out
// animation ends, in order to reset page focus, fix the underlay, etc.
// If the specified dialog isn't open then does nothing.
//
// Caller is responsible for either setting display:none on the dialog domNode,
// or calling dijit/popup.hide(), or removing it from the page DOM.
if(ds[ds.length - 1].dialog == dialog){
// Removing the top (or only) dialog in the stack, return focus
// to previous dialog
ds.pop();
var pd = ds[ds.length - 1]; // the new active dialog (or the base page itself)
// Adjust underlay
if(ds.length == 1){
// Returning to original page. Hide the underlay.
DialogUnderlay.hide();
}else{
// Popping back to previous dialog, adjust underlay.
DialogUnderlay.show(pd.underlayAttrs, pd.zIndex - 1);
}
// Adjust focus.
// TODO: regardless of setting of dialog.refocus, if the exeucte() method set focus somewhere,
// don't shift focus back to button. Note that execute() runs at the start of the fade-out but
// this code runs later, at the end of the fade-out. Menu has code like this.
if(dialog.refocus){
// If we are returning control to a previous dialog but for some reason
// that dialog didn't have a focused field, set focus to first focusable item.
// This situation could happen if two dialogs appeared at nearly the same time,
// since a dialog doesn't set it's focus until the fade-in is finished.
var focus = pd.focus;
if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
pd.dialog._getFocusItems();
focus = pd.dialog._firstFocusItem;
}
if(focus){
// Refocus the button that spawned the Dialog. This will fail in corner cases including
// page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
// before this code runs. (#15058)
try{
focus.focus();
}catch(e){
}
}
}
}else{
// Removing a dialog out of order (#9944, #10705).
// Don't need to mess with underlay or z-index or anything.
var idx = array.indexOf(array.map(ds, function(elem){
return elem.dialog;
}), dialog);
if(idx != -1){
ds.splice(idx, 1);
}
}
},
isTop: function(/*dijit/_WidgetBase*/ dialog){
// summary:
// Returns true if specified Dialog is the top in the task
return ds[ds.length - 1].dialog == dialog;
}
};
// Stack representing the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
// Each element in stack has form {
// dialog: dialogWidget,
// focus: returnFromGetFocus(),
// underlayAttrs: attributes to set on underlay (when this widget is active)
// }
var ds = Dialog._dialogStack = [
{dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
];
// If focus was accidentally removed from the dialog, such as if the user clicked a blank
// area of the screen, or clicked the browser's address bar and then tabbed into the page,
// then refocus. Won't do anything if focus was removed because the Dialog was closed, or
// because a new Dialog popped up on top of the old one, or when focus moves to popups
focus.watch("curNode", function(attr, oldNode, node){
// Note: if no dialogs, ds.length==1 but ds[ds.length-1].dialog is null
var topDialog = ds[ds.length - 1].dialog;
// If a node was focused, and there's a Dialog currently showing, and not in the process of fading out...
// Ignore focus events on other document though because it's likely an Editor inside of the Dialog.
if(node && topDialog && !topDialog._fadeOutDeferred && node.ownerDocument == topDialog.ownerDocument){
// If the node that was focused is inside the dialog or in a popup, even a context menu that isn't
// technically a descendant of the the dialog, don't do anything.
do{
if(node == topDialog.domNode || domClass.contains(node, "dijitPopup")){ return; }
}while(node = node.parentNode);
// Otherwise, return focus to the dialog. Use a delay to avoid confusing dijit/focus code's
// own tracking of focus.
topDialog.focus();
}
});
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/TooltipDialog"];
require(requires); // use indirection so modules not rolled into a build
});
}
return Dialog;
});
@@ -1,65 +1,171 @@
//>>built
define("dijit/DialogUnderlay",["dojo/_base/declare","dojo/_base/lang","dojo/aspect","dojo/dom-attr","dojo/dom-style","dojo/on","dojo/window","./_Widget","./_TemplatedMixin","./BackgroundIframe","./Viewport","./main"],function(_1,_2,_3,_4,_5,on,_6,_7,_8,_9,_a,_b){
var _c=_1("dijit.DialogUnderlay",[_7,_8],{templateString:"<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' tabIndex='-1' data-dojo-attach-point='node'></div></div>",dialogId:"","class":"",_modalConnects:[],_setDialogIdAttr:function(id){
_4.set(this.node,"id",id+"_underlay");
this._set("dialogId",id);
},_setClassAttr:function(_d){
this.node.className="dijitDialogUnderlay "+_d;
this._set("class",_d);
},postCreate:function(){
this.ownerDocumentBody.appendChild(this.domNode);
this.own(on(this.domNode,"keydown",_2.hitch(this,"_onKeyDown")));
this.inherited(arguments);
},layout:function(){
var is=this.node.style,os=this.domNode.style;
os.display="none";
var _e=_6.getBox(this.ownerDocument);
os.top=_e.t+"px";
os.left=_e.l+"px";
is.width=_e.w+"px";
is.height=_e.h+"px";
os.display="block";
},show:function(){
this.domNode.style.display="block";
this.open=true;
this.layout();
this.bgIframe=new _9(this.domNode);
var _f=_6.get(this.ownerDocument);
this._modalConnects=[_a.on("resize",_2.hitch(this,"layout")),on(_f,"scroll",_2.hitch(this,"layout"))];
},hide:function(){
this.bgIframe.destroy();
delete this.bgIframe;
this.domNode.style.display="none";
while(this._modalConnects.length){
(this._modalConnects.pop()).remove();
}
this.open=false;
},destroy:function(){
while(this._modalConnects.length){
(this._modalConnects.pop()).remove();
}
this.inherited(arguments);
},_onKeyDown:function(){
}});
_c.show=function(_10,_11){
var _12=_c._singleton;
if(!_12||_12._destroyed){
_12=_b._underlay=_c._singleton=new _c(_10);
}else{
if(_10){
_12.set(_10);
}
}
_5.set(_12.domNode,"zIndex",_11);
if(!_12.open){
_12.show();
}
};
_c.hide=function(){
var _13=_c._singleton;
if(_13&&!_13._destroyed){
_13.hide();
}
};
return _c;
define([
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch
"dojo/aspect", // aspect.after
"dojo/dom-attr", // domAttr.set
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/on",
"dojo/window", // winUtils.getBox, winUtils.get
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"./Viewport",
"./main" // for back-compat, exporting dijit._underlay (remove in 2.0)
], function(declare, lang, aspect, domAttr, domStyle, on,
winUtils, _Widget, _TemplatedMixin, BackgroundIframe, Viewport, dijit){
// module:
// dijit/DialogUnderlay
var DialogUnderlay = declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
// summary:
// A component used to block input behind a `dijit/Dialog`.
//
// Normally this class should not be instantiated directly, but rather shown and hidden via
// DialogUnderlay.show() and DialogUnderlay.hide(). And usually the module is not accessed directly
// at all, since the underlay is shown and hidden by Dialog.DialogLevelManager.
//
// The underlay itself can be styled based on and id:
// | #myDialog_underlay { background-color:red; }
//
// In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
// suffixed with _underlay.
// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
// Inner div has opacity specified in CSS file.
templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' tabIndex='-1' data-dojo-attach-point='node'></div></div>",
// Parameters on creation or updatable later
// dialogId: String
// Id of the dialog.... DialogUnderlay's id is based on this id
dialogId: "",
// class: String
// This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
"class": "",
// This will get overwritten as soon as show() is call, but leave an empty array in case hide() or destroy()
// is called first. The array is shared between instances but that's OK because we never write into it.
_modalConnects: [],
_setDialogIdAttr: function(id){
domAttr.set(this.node, "id", id + "_underlay");
this._set("dialogId", id);
},
_setClassAttr: function(clazz){
this.node.className = "dijitDialogUnderlay " + clazz;
this._set("class", clazz);
},
postCreate: function(){
// Append the underlay to the body
this.ownerDocumentBody.appendChild(this.domNode);
this.own(on(this.domNode, "keydown", lang.hitch(this, "_onKeyDown")));
this.inherited(arguments);
},
layout: function(){
// summary:
// Sets the background to the size of the viewport
//
// description:
// Sets the background to the size of the viewport (rather than the size
// of the document) since we need to cover the whole browser window, even
// if the document is only a few lines long.
// tags:
// private
var is = this.node.style,
os = this.domNode.style;
// hide the background temporarily, so that the background itself isn't
// causing scrollbars to appear (might happen when user shrinks browser
// window and then we are called to resize)
os.display = "none";
// then resize and show
var viewport = winUtils.getBox(this.ownerDocument);
os.top = viewport.t + "px";
os.left = viewport.l + "px";
is.width = viewport.w + "px";
is.height = viewport.h + "px";
os.display = "block";
},
show: function(){
// summary:
// Show the dialog underlay
this.domNode.style.display = "block";
this.open = true;
this.layout();
this.bgIframe = new BackgroundIframe(this.domNode);
var win = winUtils.get(this.ownerDocument);
this._modalConnects = [
Viewport.on("resize", lang.hitch(this, "layout")),
on(win, "scroll", lang.hitch(this, "layout"))
];
},
hide: function(){
// summary:
// Hides the dialog underlay
this.bgIframe.destroy();
delete this.bgIframe;
this.domNode.style.display = "none";
while(this._modalConnects.length){ (this._modalConnects.pop()).remove(); }
this.open = false;
},
destroy: function(){
while(this._modalConnects.length){ (this._modalConnects.pop()).remove(); }
this.inherited(arguments);
},
_onKeyDown: function(){
// summary:
// Extension point so Dialog can monitor keyboard events on the underlay.
}
});
DialogUnderlay.show = function(/*Object*/ attrs, /*Number*/ zIndex){
// summary:
// Display the underlay with the given attributes set. If the underlay is already displayed,
// then adjust it's attributes as specified.
// attrs:
// The parameters to create DialogUnderlay with.
// zIndex:
// zIndex of the underlay
var underlay = DialogUnderlay._singleton;
if(!underlay || underlay._destroyed){
underlay = dijit._underlay = DialogUnderlay._singleton = new DialogUnderlay(attrs);
}else{
if(attrs){ underlay.set(attrs); }
}
domStyle.set(underlay.domNode, 'zIndex', zIndex);
if(!underlay.open){
underlay.show();
}
};
DialogUnderlay.hide = function(){
// summary:
// Hide the underlay.
// Guard code in case the underlay widget has already been destroyed
// because we are being called during page unload (when all widgets are destroyed)
var underlay = DialogUnderlay._singleton;
if(underlay && !underlay._destroyed){
underlay.hide();
}
};
return DialogUnderlay;
});
@@ -1,171 +0,0 @@
define("dijit/DialogUnderlay", [
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.hitch
"dojo/aspect", // aspect.after
"dojo/dom-attr", // domAttr.set
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/on",
"dojo/window", // winUtils.getBox, winUtils.get
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"./Viewport",
"./main" // for back-compat, exporting dijit._underlay (remove in 2.0)
], function(declare, lang, aspect, domAttr, domStyle, on,
winUtils, _Widget, _TemplatedMixin, BackgroundIframe, Viewport, dijit){
// module:
// dijit/DialogUnderlay
var DialogUnderlay = declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
// summary:
// A component used to block input behind a `dijit/Dialog`.
//
// Normally this class should not be instantiated directly, but rather shown and hidden via
// DialogUnderlay.show() and DialogUnderlay.hide(). And usually the module is not accessed directly
// at all, since the underlay is shown and hidden by Dialog.DialogLevelManager.
//
// The underlay itself can be styled based on and id:
// | #myDialog_underlay { background-color:red; }
//
// In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
// suffixed with _underlay.
// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
// Inner div has opacity specified in CSS file.
templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' tabIndex='-1' data-dojo-attach-point='node'></div></div>",
// Parameters on creation or updatable later
// dialogId: String
// Id of the dialog.... DialogUnderlay's id is based on this id
dialogId: "",
// class: String
// This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
"class": "",
// This will get overwritten as soon as show() is call, but leave an empty array in case hide() or destroy()
// is called first. The array is shared between instances but that's OK because we never write into it.
_modalConnects: [],
_setDialogIdAttr: function(id){
domAttr.set(this.node, "id", id + "_underlay");
this._set("dialogId", id);
},
_setClassAttr: function(clazz){
this.node.className = "dijitDialogUnderlay " + clazz;
this._set("class", clazz);
},
postCreate: function(){
// Append the underlay to the body
this.ownerDocumentBody.appendChild(this.domNode);
this.own(on(this.domNode, "keydown", lang.hitch(this, "_onKeyDown")));
this.inherited(arguments);
},
layout: function(){
// summary:
// Sets the background to the size of the viewport
//
// description:
// Sets the background to the size of the viewport (rather than the size
// of the document) since we need to cover the whole browser window, even
// if the document is only a few lines long.
// tags:
// private
var is = this.node.style,
os = this.domNode.style;
// hide the background temporarily, so that the background itself isn't
// causing scrollbars to appear (might happen when user shrinks browser
// window and then we are called to resize)
os.display = "none";
// then resize and show
var viewport = winUtils.getBox(this.ownerDocument);
os.top = viewport.t + "px";
os.left = viewport.l + "px";
is.width = viewport.w + "px";
is.height = viewport.h + "px";
os.display = "block";
},
show: function(){
// summary:
// Show the dialog underlay
this.domNode.style.display = "block";
this.open = true;
this.layout();
this.bgIframe = new BackgroundIframe(this.domNode);
var win = winUtils.get(this.ownerDocument);
this._modalConnects = [
Viewport.on("resize", lang.hitch(this, "layout")),
on(win, "scroll", lang.hitch(this, "layout"))
];
},
hide: function(){
// summary:
// Hides the dialog underlay
this.bgIframe.destroy();
delete this.bgIframe;
this.domNode.style.display = "none";
while(this._modalConnects.length){ (this._modalConnects.pop()).remove(); }
this.open = false;
},
destroy: function(){
while(this._modalConnects.length){ (this._modalConnects.pop()).remove(); }
this.inherited(arguments);
},
_onKeyDown: function(){
// summary:
// Extension point so Dialog can monitor keyboard events on the underlay.
}
});
DialogUnderlay.show = function(/*Object*/ attrs, /*Number*/ zIndex){
// summary:
// Display the underlay with the given attributes set. If the underlay is already displayed,
// then adjust it's attributes as specified.
// attrs:
// The parameters to create DialogUnderlay with.
// zIndex:
// zIndex of the underlay
var underlay = DialogUnderlay._singleton;
if(!underlay || underlay._destroyed){
underlay = dijit._underlay = DialogUnderlay._singleton = new DialogUnderlay(attrs);
}else{
if(attrs){ underlay.set(attrs); }
}
domStyle.set(underlay.domNode, 'zIndex', zIndex);
if(!underlay.open){
underlay.show();
}
};
DialogUnderlay.hide = function(){
// summary:
// Hide the underlay.
// Guard code in case the underlay widget has already been destroyed
// because we are being called during page unload (when all widgets are destroyed)
var underlay = DialogUnderlay._singleton;
if(underlay && !underlay._destroyed){
underlay.hide();
}
};
return DialogUnderlay;
});
@@ -1,24 +1,44 @@
//>>built
require({cache:{"url:dijit/templates/Menu.html":"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
define("dijit/DropDownMenu",["dojo/_base/declare","dojo/keys","dojo/text!./templates/Menu.html","./_MenuBase"],function(_1,_2,_3,_4){
return _1("dijit.DropDownMenu",_4,{templateString:_3,baseClass:"dijitMenu",_onUpArrow:function(){
this.focusPrev();
},_onDownArrow:function(){
this.focusNext();
},_onRightArrow:function(_5){
this._moveToPopup(_5);
_5.stopPropagation();
_5.preventDefault();
},_onLeftArrow:function(_6){
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
_6.stopPropagation();
_6.preventDefault();
}
}});
define([
"dojo/_base/declare", // declare
"dojo/keys", // keys
"dojo/text!./templates/Menu.html",
"./_MenuBase"
], function(declare, keys, template, _MenuBase){
// module:
// dijit/DropDownMenu
return declare("dijit.DropDownMenu", _MenuBase, {
// summary:
// A menu, without features for context menu (Meaning, drop down menu)
templateString: template,
baseClass: "dijitMenu",
// Arrow key navigation
_onUpArrow: function(){
this.focusPrev();
},
_onDownArrow: function(){
this.focusNext();
},
_onRightArrow: function(/*Event*/ evt){
this._moveToPopup(evt);
evt.stopPropagation();
evt.preventDefault();
},
_onLeftArrow: function(/*Event*/ evt){
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
evt.stopPropagation();
evt.preventDefault();
}
}
});
});
@@ -1,46 +0,0 @@
require({cache:{
'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
define("dijit/DropDownMenu", [
"dojo/_base/declare", // declare
"dojo/keys", // keys
"dojo/text!./templates/Menu.html",
"./_MenuBase"
], function(declare, keys, template, _MenuBase){
// module:
// dijit/DropDownMenu
return declare("dijit.DropDownMenu", _MenuBase, {
// summary:
// A menu, without features for context menu (Meaning, drop down menu)
templateString: template,
baseClass: "dijitMenu",
// Arrow key navigation
_onUpArrow: function(){
this.focusPrev();
},
_onDownArrow: function(){
this.focusNext();
},
_onRightArrow: function(/*Event*/ evt){
this._moveToPopup(evt);
evt.stopPropagation();
evt.preventDefault();
},
_onLeftArrow: function(/*Event*/ evt){
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
evt.stopPropagation();
evt.preventDefault();
}
}
});
});
File diff suppressed because it is too large Load Diff
@@ -1,878 +0,0 @@
define("dijit/Editor", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/Deferred", // Deferred
"dojo/i18n", // i18n.getLocalization
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add
"dojo/dom-geometry",
"dojo/dom-style", // domStyle.set, get
"dojo/keys", // keys.F1 keys.F15 keys.TAB
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/sniff", // has("ie") has("mac") has("webkit")
"dojo/string", // string.substitute
"dojo/topic", // topic.publish()
"./_Container",
"./Toolbar",
"./ToolbarSeparator",
"./layout/_LayoutWidget",
"./form/ToggleButton",
"./_editor/_Plugin",
"./_editor/plugins/EnterKeyHandling",
"./_editor/html",
"./_editor/range",
"./_editor/RichText",
"./main", // dijit._scopeName
"dojo/i18n!./_editor/nls/commands"
], function(require, array, declare, Deferred, i18n, domAttr, domClass, domGeometry, domStyle,
keys, lang, has, string, topic,
_Container, Toolbar, ToolbarSeparator, _LayoutWidget, ToggleButton,
_Plugin, EnterKeyHandling, html, rangeapi, RichText, dijit){
// module:
// dijit/Editor
var Editor = declare("dijit.Editor", RichText, {
// summary:
// A rich text Editing widget
//
// description:
// This widget provides basic WYSIWYG editing features, based on the browser's
// underlying rich text editing capability, accompanied by a toolbar (`dijit.Toolbar`).
// A plugin model is available to extend the editor's capabilities as well as the
// the options available in the toolbar. Content generation may vary across
// browsers, and clipboard operations may have different results, to name
// a few limitations. Note: this widget should not be used with the HTML
// &lt;TEXTAREA&gt; tag -- see dijit/_editor/RichText for details.
// plugins: [const] Object[]
// A list of plugin names (as strings) or instances (as objects)
// for this widget.
//
// When declared in markup, it might look like:
// | plugins="['bold',{name:'dijit._editor.plugins.FontChoice', command:'fontName', generic:true}]"
plugins: null,
// extraPlugins: [const] Object[]
// A list of extra plugin names which will be appended to plugins array
extraPlugins: null,
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Initial settings for any of the attributes, except readonly attributes.
// srcNodeRef: DOMNode
// The editor replaces the specified DOMNode.
if(!lang.isArray(this.plugins)){
this.plugins = ["undo", "redo", "|", "cut", "copy", "paste", "|", "bold", "italic", "underline", "strikethrough", "|",
"insertOrderedList", "insertUnorderedList", "indent", "outdent", "|", "justifyLeft", "justifyRight", "justifyCenter", "justifyFull",
EnterKeyHandling /*, "createLink"*/];
}
this._plugins = [];
this._editInterval = this.editActionInterval * 1000;
//IE will always lose focus when other element gets focus, while for FF and safari,
//when no iframe is used, focus will be lost whenever another element gets focus.
//For IE, we can connect to onBeforeDeactivate, which will be called right before
//the focus is lost, so we can obtain the selected range. For other browsers,
//no equivalent of onBeforeDeactivate, so we need to do two things to make sure
//selection is properly saved before focus is lost: 1) when user clicks another
//element in the page, in which case we listen to mousedown on the entire page and
//see whether user clicks out of a focus editor, if so, save selection (focus will
//only lost after onmousedown event is fired, so we can obtain correct caret pos.)
//2) when user tabs away from the editor, which is handled in onKeyDown below.
if(has("ie") || has("trident")){
this.events.push("onBeforeDeactivate");
this.events.push("onBeforeActivate");
}
},
postMixInProperties: function(){
// summary:
// Extension to make sure a deferred is in place before certain functions
// execute, like making sure all the plugins are properly inserted.
// Set up a deferred so that the value isn't applied to the editor
// until all the plugins load, needed to avoid timing condition
// reported in #10537.
this.setValueDeferred = new Deferred();
this.inherited(arguments);
},
postCreate: function(){
this.inherited(arguments);
//for custom undo/redo, if enabled.
this._steps = this._steps.slice(0);
this._undoedSteps = this._undoedSteps.slice(0);
if(lang.isArray(this.extraPlugins)){
this.plugins = this.plugins.concat(this.extraPlugins);
}
this.commands = i18n.getLocalization("dijit._editor", "commands", this.lang);
if(has("webkit")){
// Disable selecting the entire editor by inadvertent double-clicks.
// on buttons, title bar, etc. Otherwise clicking too fast on
// a button such as undo/redo selects the entire editor.
domStyle.set(this.domNode, "KhtmlUserSelect", "none");
}
},
startup: function(){
this.inherited(arguments);
if(!this.toolbar){
// if we haven't been assigned a toolbar, create one
this.toolbar = new Toolbar({
ownerDocument: this.ownerDocument,
dir: this.dir,
lang: this.lang,
"aria-label": this.id
});
this.header.appendChild(this.toolbar.domNode);
}
array.forEach(this.plugins, this.addPlugin, this);
// Okay, denote the value can now be set.
this.setValueDeferred.resolve(true);
domClass.add(this.iframe.parentNode, "dijitEditorIFrameContainer");
domClass.add(this.iframe, "dijitEditorIFrame");
domAttr.set(this.iframe, "allowTransparency", true);
this.toolbar.startup();
this.onNormalizedDisplayChanged(); //update toolbar button status
},
destroy: function(){
array.forEach(this._plugins, function(p){
if(p && p.destroy){
p.destroy();
}
});
this._plugins = [];
this.toolbar.destroyRecursive();
delete this.toolbar;
this.inherited(arguments);
},
addPlugin: function(/*String||Object||Function*/ plugin, /*Integer?*/ index){
// summary:
// takes a plugin name as a string or a plugin instance and
// adds it to the toolbar and associates it with this editor
// instance. The resulting plugin is added to the Editor's
// plugins array. If index is passed, it's placed in the plugins
// array at that index. No big magic, but a nice helper for
// passing in plugin names via markup.
// plugin:
// String, args object, plugin instance, or plugin constructor
// args:
// This object will be passed to the plugin constructor
// index:
// Used when creating an instance from
// something already in this.plugins. Ensures that the new
// instance is assigned to this.plugins at that index.
var args = lang.isString(plugin) ? {name: plugin} : lang.isFunction(plugin) ? {ctor: plugin} : plugin;
if(!args.setEditor){
var o = {"args": args, "plugin": null, "editor": this};
if(args.name){
// search registry for a plugin factory matching args.name, if it's not there then
// fallback to 1.0 API:
// ask all loaded plugin modules to fill in o.plugin if they can (ie, if they implement args.name)
// remove fallback for 2.0.
if(_Plugin.registry[args.name]){
o.plugin = _Plugin.registry[args.name](args);
}else{
topic.publish(dijit._scopeName + ".Editor.getPlugin", o); // publish
}
}
if(!o.plugin){
try{
// TODO: remove lang.getObject() call in 2.0
var pc = args.ctor || lang.getObject(args.name) || require(args.name);
if(pc){
o.plugin = new pc(args);
}
}catch(e){
throw new Error(this.id + ": cannot find plugin [" + args.name + "]");
}
}
if(!o.plugin){
throw new Error(this.id + ": cannot find plugin [" + args.name + "]");
}
plugin = o.plugin;
}
if(arguments.length > 1){
this._plugins[index] = plugin;
}else{
this._plugins.push(plugin);
}
plugin.setEditor(this);
if(lang.isFunction(plugin.setToolbar)){
plugin.setToolbar(this.toolbar);
}
},
//the following 2 functions are required to make the editor play nice under a layout widget, see #4070
resize: function(size){
// summary:
// Resize the editor to the specified size, see `dijit/layout/_LayoutWidget.resize()`
if(size){
// we've been given a height/width for the entire editor (toolbar + contents), calls layout()
// to split the allocated size between the toolbar and the contents
_LayoutWidget.prototype.resize.apply(this, arguments);
}
/*
else{
// do nothing, the editor is already laid out correctly. The user has probably specified
// the height parameter, which was used to set a size on the iframe
}
*/
},
layout: function(){
// summary:
// Called from `dijit/layout/_LayoutWidget.resize()`. This shouldn't be called directly
// tags:
// protected
// Converts the iframe (or rather the <div> 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() +
domGeometry.getPadBorderExtents(this.iframe.parentNode).h +
domGeometry.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.
if(/^rtl$/i.test(b.dir || "")){
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"){
this.defer("placeCursorAtEnd");
}
this.inherited(arguments);
}
},
onBeforeActivate: function(){
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){
this._editTimer.remove();
}
this._editTimer = this.defer("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 = this.inherited(arguments);
if(this.customUndo){
this._endEditing();
}
return r;
}
},
_pasteImpl: function(){
// summary:
// Over-ride of paste command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("paste");
},
_cutImpl: function(){
// summary:
// Over-ride of cut command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("cut");
},
_copyImpl: function(){
// summary:
// Over-ride of copy command control to make execCommand cleaner
// tags:
// Protected
return this._clipboardCommand("copy");
},
_clipboardCommand: function(cmd){
// summary:
// Function to handle processing clipboard commands (or at least try to).
// tags:
// Private
var r;
try{
// Try to exec the superclass exec-command and see if it works.
r = this.document.execCommand(cmd, false, null);
if(has("webkit") && !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 */ ||
(e.code == 9 && has("opera") /* Opera not supported */)){
// Warn user of platform limitation. Cannot programmatically access clipboard. See ticket #4136
var sub = string.substitute,
accel = {cut: 'X', copy: 'C', paste: 'V'};
alert(sub(this.commands.systemShortcut,
[this.commands[cmd], sub(this.commands[has("mac") ? 'appleKey' : 'ctrlKey'], [accel[cmd]])]));
}
r = false;
}
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(has("ie") < 9 || (has("ie") === 9 && has("quirks"))){
if(lang.isArray(mark)){
// IE CONTROL, have to use the native bookmark.
bookmark = [];
array.forEach(mark, function(n){
bookmark.push(rangeapi.getNode(n, this.editNode));
}, this);
this.selection.moveToBookmark({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 = rangeapi.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = rangeapi.create(this.window);
sNode = rangeapi.getNode(mark.startContainer, this.editNode);
eNode = rangeapi.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 <p> 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 = rangeapi.getSelection(this.window);
if(sel && sel.removeAllRanges){
sel.removeAllRanges();
r = rangeapi.create(this.window);
sNode = rangeapi.getNode(mark.startContainer, this.editNode);
eNode = rangeapi.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
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
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){
this._editTimer = this._editTimer.remove();
}
if(this._inEditing){
this._endEditing(ignore_caret);
this._inEditing = false;
}
},
_getBookmark: function(){
// summary:
// Get the currently selected text
// tags:
// protected
var b = this.selection.getBookmark();
var tmp = [];
if(b && b.mark){
var mark = b.mark;
if(has("ie") < 9 || (has("ie") === 9 && has("quirks"))){
// Try to use the pseudo range API on IE for better accuracy.
var sel = rangeapi.getSelection(this.window);
if(!lang.isArray(mark)){
if(sel){
var range;
if(sel.rangeCount){
range = sel.getRangeAt(0);
}
if(range){
b.mark = range.cloneRange();
}else{
b.mark = this.selection.getBookmark();
}
}
}else{
// Control ranges (img, table, etc), handle differently.
array.forEach(b.mark, function(n){
tmp.push(rangeapi.getIndex(n, this.editNode).o);
}, this);
b.mark = tmp;
}
}
try{
if(b.mark && b.mark.startContainer){
tmp = rangeapi.getIndex(b.mark.startContainer, this.editNode).o;
b.mark = {startContainer: tmp,
startOffset: b.mark.startOffset,
endContainer: b.mark.endContainer === b.mark.startContainer ? tmp : rangeapi.getIndex(b.mark.endContainer, this.editNode).o,
endOffset: b.mark.endOffset};
}
}catch(e){
b.mark = null;
}
}
return b;
},
_beginEditing: function(){
// 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': html.getChildrenHtml(this.editNode), 'bookmark': this._getBookmark()});
}
},
_endEditing: function(){
// 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 = html.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(!has("ie") && !this.iframe && e.keyCode == keys.TAB && !this.tabIndent){
this._saveSelection();
}
if(!this.customUndo){
this.inherited(arguments);
return;
}
var k = e.keyCode;
if(e.ctrlKey && !e.shiftKey && !e.altKey){//undo and redo only if the special right Alt + z/y are not pressed #5892
if(k == 90 || k == 122){ //z, but also F11 key
e.stopPropagation();
e.preventDefault();
this.undo();
return;
}else if(k == 89 || k == 121){ //y
e.stopPropagation();
e.preventDefault();
this.redo();
return;
}
}
this.inherited(arguments);
switch(k){
case keys.ENTER:
case keys.BACKSPACE:
case keys.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');
}else{
this.beginEditing('paste');
}
//use timeout to trigger after the paste is complete
this.defer("endEditing", 1);
break;
}
//pass through
default:
if(!e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode < keys.F1 || e.keyCode > keys.F15)){
this.beginEditing();
break;
}
//pass through
case keys.ALT:
this.endEditing();
break;
case keys.UP_ARROW:
case keys.DOWN_ARROW:
case keys.LEFT_ARROW:
case keys.RIGHT_ARROW:
case keys.HOME:
case keys.END:
case keys.PAGE_UP:
case keys.PAGE_DOWN:
this.endEditing(true);
break;
//maybe ctrl+backspace/delete, so don't endEditing when ctrl is pressed
case keys.CTRL:
case keys.SHIFT:
case keys.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 simultaneously. */
}
},
_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(this.selection.isCollapsed()){
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 maintenance.
// tags:
// protected
if(!this.customUndo){
this.inherited(arguments);
}else{
if(this.isClosed){
this.setValue(html);
}else{
this.beginEditing();
if(!html){
html = "&#160;"; // &nbsp;
}
this.setValue(html);
this.endEditing();
}
}
},
_setDisabledAttr: function(/*Boolean*/ value){
this.setValueDeferred.then(lang.hitch(this, function(){
if((!this.disabled && value) || (!this._buttonEnabledPlugins && value)){
// Disable editor: disable all enabled buttons and remember that list
array.forEach(this._plugins, function(p){
p.set("disabled", true);
});
}else if(this.disabled && !value){
// Restore plugins to being active.
array.forEach(this._plugins, function(p){
p.set("disabled", false);
});
}
}));
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){
domStyle.set(this.document.body, "color", domStyle.get(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
function simplePluginFactory(args){
return new _Plugin({ command: args.name });
}
function togglePluginFactory(args){
return new _Plugin({ buttonClass: ToggleButton, command: args.name });
}
lang.mixin(_Plugin.registry, {
"undo": simplePluginFactory,
"redo": simplePluginFactory,
"cut": simplePluginFactory,
"copy": simplePluginFactory,
"paste": simplePluginFactory,
"insertOrderedList": simplePluginFactory,
"insertUnorderedList": simplePluginFactory,
"indent": simplePluginFactory,
"outdent": simplePluginFactory,
"justifyCenter": simplePluginFactory,
"justifyFull": simplePluginFactory,
"justifyLeft": simplePluginFactory,
"justifyRight": simplePluginFactory,
"delete": simplePluginFactory,
"selectAll": simplePluginFactory,
"removeFormat": simplePluginFactory,
"unlink": simplePluginFactory,
"insertHorizontalRule": simplePluginFactory,
"bold": togglePluginFactory,
"italic": togglePluginFactory,
"underline": togglePluginFactory,
"strikethrough": togglePluginFactory,
"subscript": togglePluginFactory,
"superscript": togglePluginFactory,
"|": function(){
return new _Plugin({
setEditor: function(editor){
this.editor = editor;
this.button = new ToolbarSeparator({ownerDocument: editor.ownerDocument});
}
});
}
});
return Editor;
});
@@ -1,14 +1,41 @@
//>>built
require({cache:{"url:dijit/templates/Fieldset.html":"<fieldset>\n\t<legend data-dojo-attach-event=\"ondijitclick:_onTitleClick, onkeydown:_onTitleKey\"\n\t\t\tdata-dojo-attach-point=\"titleBarNode, titleNode\">\n\t\t<span data-dojo-attach-point=\"arrowNode\" class=\"dijitInline dijitArrowNode\" role=\"presentation\"></span\n\t\t><span data-dojo-attach-point=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t><span data-dojo-attach-point=\"titleNode, focusNode\" class=\"dijitFieldsetLegendNode\" id=\"${id}_titleNode\"></span>\n\t</legend>\n\t<div class=\"dijitFieldsetContentOuter\" data-dojo-attach-point=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" data-dojo-attach-point=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitFieldsetContentInner\" data-dojo-attach-point=\"containerNode\" role=\"region\"\n\t\t\t\t \tid=\"${id}_pane\" aria-labelledby=\"${id}_titleNode\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</fieldset>\n"}});
define("dijit/Fieldset",["dojo/_base/declare","dojo/query!css2","dijit/TitlePane","dojo/text!./templates/Fieldset.html","./a11yclick"],function(_1,_2,_3,_4){
return _1("dijit.Fieldset",_3,{baseClass:"dijitFieldset",title:"",open:true,templateString:_4,postCreate:function(){
if(!this.title){
var _5=_2("legend",this.containerNode);
if(_5.length){
this.set("title",_5[0].innerHTML);
_5[0].parentNode.removeChild(_5[0]);
}
}
this.inherited(arguments);
}});
define([
"dojo/_base/declare",
"dojo/query!css2",
"dijit/TitlePane",
"dojo/text!./templates/Fieldset.html",
"./a11yclick" // template uses ondijitclick
], function(declare, query, TitlePane, template){
return declare("dijit.Fieldset", TitlePane, {
// summary:
// An accessible fieldset that can be expanded or collapsed via
// its legend. Fieldset extends `dijit.TitlePane`.
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: 'dijitFieldset',
// title: String
// Content of the legend tag. Overrides <legend> tag if not empty.
title: '',
// open: Boolean
// Whether fieldset is opened or closed.
open: true,
templateString: template,
postCreate: function() {
if(!this.title){
var legends = query('legend', this.containerNode);
if(legends.length) { // oops, no legend?
this.set('title', legends[0].innerHTML);
legends[0].parentNode.removeChild(legends[0]);
}
}
this.inherited(arguments);
}
});
});
@@ -1,43 +0,0 @@
require({cache:{
'url:dijit/templates/Fieldset.html':"<fieldset>\n\t<legend data-dojo-attach-event=\"ondijitclick:_onTitleClick, onkeydown:_onTitleKey\"\n\t\t\tdata-dojo-attach-point=\"titleBarNode, titleNode\">\n\t\t<span data-dojo-attach-point=\"arrowNode\" class=\"dijitInline dijitArrowNode\" role=\"presentation\"></span\n\t\t><span data-dojo-attach-point=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t><span data-dojo-attach-point=\"titleNode, focusNode\" class=\"dijitFieldsetLegendNode\" id=\"${id}_titleNode\"></span>\n\t</legend>\n\t<div class=\"dijitFieldsetContentOuter\" data-dojo-attach-point=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" data-dojo-attach-point=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitFieldsetContentInner\" data-dojo-attach-point=\"containerNode\" role=\"region\"\n\t\t\t\t \tid=\"${id}_pane\" aria-labelledby=\"${id}_titleNode\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</fieldset>\n"}});
define("dijit/Fieldset", [
"dojo/_base/declare",
"dojo/query!css2",
"dijit/TitlePane",
"dojo/text!./templates/Fieldset.html",
"./a11yclick" // template uses ondijitclick
], function(declare, query, TitlePane, template){
return declare("dijit.Fieldset", TitlePane, {
// summary:
// An accessible fieldset that can be expanded or collapsed via
// its legend. Fieldset extends `dijit.TitlePane`.
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: 'dijitFieldset',
// title: String
// Content of the legend tag. Overrides <legend> tag if not empty.
title: '',
// open: Boolean
// Whether fieldset is opened or closed.
open: true,
templateString: template,
postCreate: function() {
if(!this.title){
var legends = query('legend', this.containerNode);
if(legends.length) { // oops, no legend?
this.set('title', legends[0].innerHTML);
legends[0].parentNode.removeChild(legends[0]);
}
}
this.inherited(arguments);
}
});
});
@@ -1,249 +1,658 @@
//>>built
require({cache:{"url:dijit/templates/InlineEditBox.html":"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"./form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"./form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
define("dijit/InlineEditBox",["require","dojo/_base/array","dojo/aspect","dojo/_base/declare","dojo/dom-attr","dojo/dom-class","dojo/dom-construct","dojo/dom-style","dojo/i18n","dojo/_base/kernel","dojo/keys","dojo/_base/lang","dojo/on","dojo/sniff","dojo/when","./a11yclick","./focus","./_Widget","./_TemplatedMixin","./_WidgetsInTemplateMixin","./_Container","./form/Button","./form/_TextBoxMixin","./form/TextBox","dojo/text!./templates/InlineEditBox.html","dojo/i18n!./nls/common"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c,on,_d,_e,_f,fm,_10,_11,_12,_13,_14,_15,_16,_17){
var _18=_4("dijit._InlineEditor",[_10,_11,_12],{templateString:_17,contextRequire:_1,postMixInProperties:function(){
this.inherited(arguments);
this.messages=_9.getLocalization("dijit","common",this.lang);
_2.forEach(["buttonSave","buttonCancel"],function(_19){
if(!this[_19]){
this[_19]=this.messages[_19];
}
},this);
},buildRendering:function(){
this.inherited(arguments);
var Cls=typeof this.editor=="string"?(_c.getObject(this.editor)||_1(this.editor)):this.editor;
var _1a=this.sourceStyle,_1b="line-height:"+_1a.lineHeight+";",_1c=_8.getComputedStyle(this.domNode);
_2.forEach(["Weight","Family","Size","Style"],function(_1d){
var _1e=_1a["font"+_1d],_1f=_1c["font"+_1d];
if(_1f!=_1e){
_1b+="font-"+_1d+":"+_1a["font"+_1d]+";";
}
},this);
_2.forEach(["marginTop","marginBottom","marginLeft","marginRight","position","left","top","right","bottom","float","clear","display"],function(_20){
this.domNode.style[_20]=_1a[_20];
},this);
var _21=this.inlineEditBox.width;
if(_21=="100%"){
_1b+="width:100%;";
this.domNode.style.display="block";
}else{
_1b+="width:"+(_21+(Number(_21)==_21?"px":""))+";";
}
var _22=_c.delegate(this.inlineEditBox.editorParams,{style:_1b,dir:this.dir,lang:this.lang,textDir:this.textDir});
this.editWidget=new Cls(_22,this.editorPlaceholder);
if(this.inlineEditBox.autoSave){
this.saveButton.destroy();
this.cancelButton.destroy();
this.saveButton=this.cancelButton=null;
_7.destroy(this.buttonContainer);
}
},postCreate:function(){
this.inherited(arguments);
var ew=this.editWidget;
if(this.inlineEditBox.autoSave){
this.own(_3.after(ew,"onChange",_c.hitch(this,"_onChange"),true),on(ew,"keydown",_c.hitch(this,"_onKeyDown")));
}else{
if("intermediateChanges" in ew){
ew.set("intermediateChanges",true);
this.own(_3.after(ew,"onChange",_c.hitch(this,"_onIntermediateChange"),true));
this.saveButton.set("disabled",true);
}
}
},startup:function(){
this.editWidget.startup();
this.inherited(arguments);
},_onIntermediateChange:function(){
this.saveButton.set("disabled",(this.getValue()==this._resetValue)||!this.enableSave());
},destroy:function(){
this.editWidget.destroy(true);
this.inherited(arguments);
},getValue:function(){
var ew=this.editWidget;
return String(ew.get(("displayedValue" in ew||"_getDisplayedValueAttr" in ew)?"displayedValue":"value"));
},_onKeyDown:function(e){
if(this.inlineEditBox.autoSave&&this.inlineEditBox.editing){
if(e.altKey||e.ctrlKey){
return;
}
if(e.keyCode==_b.ESCAPE){
e.stopPropagation();
e.preventDefault();
this.cancel(true);
}else{
if(e.keyCode==_b.ENTER&&e.target.tagName=="INPUT"){
e.stopPropagation();
e.preventDefault();
this._onChange();
}
}
}
},_onBlur:function(){
this.inherited(arguments);
if(this.inlineEditBox.autoSave&&this.inlineEditBox.editing){
if(this.getValue()==this._resetValue){
this.cancel(false);
}else{
if(this.enableSave()){
this.save(false);
}
}
}
},_onChange:function(){
if(this.inlineEditBox.autoSave&&this.inlineEditBox.editing&&this.enableSave()){
fm.focus(this.inlineEditBox.displayNode);
}
},enableSave:function(){
return this.editWidget.isValid?this.editWidget.isValid():true;
},focus:function(){
this.editWidget.focus();
if(this.editWidget.focusNode){
fm._onFocusNode(this.editWidget.focusNode);
if(this.editWidget.focusNode.tagName=="INPUT"){
this.defer(function(){
_15.selectInputText(this.editWidget.focusNode);
});
}
}
}});
var _23=_4("dijit.InlineEditBox"+(_d("dojo-bidi")?"_NoBidi":""),_10,{editing:false,autoSave:true,buttonSave:"",buttonCancel:"",renderAsHtml:false,editor:_16,editorWrapper:_18,editorParams:{},disabled:false,onChange:function(){
},onCancel:function(){
},width:"100%",value:"",noValueIndicator:_d("ie")<=6?"<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>":"<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>",constructor:function(){
this.editorParams={};
},postMixInProperties:function(){
this.inherited(arguments);
this.displayNode=this.srcNodeRef;
this.own(on(this.displayNode,_f,_c.hitch(this,"_onClick")),on(this.displayNode,"mouseover, focus",_c.hitch(this,"_onMouseOver")),on(this.displayNode,"mouseout, blur",_c.hitch(this,"_onMouseOut")));
this.displayNode.setAttribute("role","button");
if(!this.displayNode.getAttribute("tabIndex")){
this.displayNode.setAttribute("tabIndex",0);
}
if(!this.value&&!("value" in this.params)){
this.value=_c.trim(this.renderAsHtml?this.displayNode.innerHTML:(this.displayNode.innerText||this.displayNode.textContent||""));
}
if(!this.value){
this.displayNode.innerHTML=this.noValueIndicator;
}
_6.add(this.displayNode,"dijitInlineEditBoxDisplayMode");
},setDisabled:function(_24){
_a.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.","","2.0");
this.set("disabled",_24);
},_setDisabledAttr:function(_25){
this.domNode.setAttribute("aria-disabled",_25?"true":"false");
if(_25){
this.displayNode.removeAttribute("tabIndex");
}else{
this.displayNode.setAttribute("tabIndex",0);
}
_6.toggle(this.displayNode,"dijitInlineEditBoxDisplayModeDisabled",_25);
this._set("disabled",_25);
},_onMouseOver:function(){
if(!this.disabled){
_6.add(this.displayNode,"dijitInlineEditBoxDisplayModeHover");
}
},_onMouseOut:function(){
_6.remove(this.displayNode,"dijitInlineEditBoxDisplayModeHover");
},_onClick:function(e){
if(this.disabled){
return;
}
if(e){
e.stopPropagation();
e.preventDefault();
}
this._onMouseOut();
this.defer("edit");
},edit:function(){
if(this.disabled||this.editing){
return;
}
this._set("editing",true);
this._savedTabIndex=_5.get(this.displayNode,"tabIndex")||"0";
if(!this.wrapperWidget){
var _26=_7.create("span",null,this.domNode,"before");
var Ewc=typeof this.editorWrapper=="string"?_c.getObject(this.editorWrapper):this.editorWrapper;
this.wrapperWidget=new Ewc({value:this.value,buttonSave:this.buttonSave,buttonCancel:this.buttonCancel,dir:this.dir,lang:this.lang,tabIndex:this._savedTabIndex,editor:this.editor,inlineEditBox:this,sourceStyle:_8.getComputedStyle(this.displayNode),save:_c.hitch(this,"save"),cancel:_c.hitch(this,"cancel"),textDir:this.textDir},_26);
if(!this.wrapperWidget._started){
this.wrapperWidget.startup();
}
if(!this._started){
this.startup();
}
}
var ww=this.wrapperWidget;
_6.add(this.displayNode,"dijitOffScreen");
_6.remove(ww.domNode,"dijitOffScreen");
_8.set(ww.domNode,{visibility:"visible"});
_5.set(this.displayNode,"tabIndex","-1");
var ew=ww.editWidget;
var _27=this;
_e(ew.onLoadDeferred,_c.hitch(ww,function(){
ew.set(("displayedValue" in ew||"_setDisplayedValueAttr" in ew)?"displayedValue":"value",_27.value);
this.defer(function(){
if(ww.saveButton){
ww.saveButton.set("disabled","intermediateChanges" in ew);
}
this.focus();
this._resetValue=this.getValue();
});
}));
},_onBlur:function(){
this.inherited(arguments);
if(!this.editing){
}
},destroy:function(){
if(this.wrapperWidget&&!this.wrapperWidget._destroyed){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
this.inherited(arguments);
},_showText:function(_28){
var ww=this.wrapperWidget;
_8.set(ww.domNode,{visibility:"hidden"});
_6.add(ww.domNode,"dijitOffScreen");
_6.remove(this.displayNode,"dijitOffScreen");
_5.set(this.displayNode,"tabIndex",this._savedTabIndex);
if(_28){
fm.focus(this.displayNode);
}
},save:function(_29){
if(this.disabled||!this.editing){
return;
}
this._set("editing",false);
var ww=this.wrapperWidget;
var _2a=ww.getValue();
this.set("value",_2a);
this._showText(_29);
},setValue:function(val){
_a.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.","","2.0");
return this.set("value",val);
},_setValueAttr:function(val){
val=_c.trim(val);
var _2b=this.renderAsHtml?val:val.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;").replace(/"/gm,"&quot;").replace(/\n/g,"<br>");
this.displayNode.innerHTML=_2b||this.noValueIndicator;
this._set("value",val);
if(this._started){
this.defer(function(){
this.onChange(val);
});
}
},getValue:function(){
_a.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.","","2.0");
return this.get("value");
},cancel:function(_2c){
if(this.disabled||!this.editing){
return;
}
this._set("editing",false);
this.defer("onCancel");
this._showText(_2c);
}});
if(_d("dojo-bidi")){
_23=_4("dijit.InlineEditBox",_23,{_setValueAttr:function(){
this.inherited(arguments);
this.applyTextDir(this.displayNode);
}});
}
_23._InlineEditor=_18;
return _23;
define([
"require",
"dojo/_base/array", // array.forEach
"dojo/aspect",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set domAttr.get
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
"dojo/i18n", // i18n.getLocalization
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.getObject
"dojo/on",
"dojo/sniff", // has("ie")
"dojo/when",
"./a11yclick",
"./focus",
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"./_Container",
"./form/Button",
"./form/_TextBoxMixin",
"./form/TextBox",
"dojo/text!./templates/InlineEditBox.html",
"dojo/i18n!./nls/common"
], function(require, array, aspect, declare, domAttr, domClass, domConstruct, domStyle, i18n, kernel, keys, lang, on, has, when, a11yclick, fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
// module:
// dijit/InlineEditBox
var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// Internal widget used by InlineEditBox, displayed when in editing mode
// to display the editor and maybe save/cancel buttons. Calling code should
// connect to save/cancel methods to detect when editing is finished
//
// Has mainly the same parameters as InlineEditBox, plus these values:
//
// style: Object
// Set of CSS attributes of display node, to replicate in editor
//
// value: String
// Value as an HTML string or plain text string, depending on renderAsHTML flag
templateString: template,
contextRequire: require,
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit", "common", this.lang);
array.forEach(["buttonSave", "buttonCancel"], function(prop){
if(!this[prop]){
this[prop] = this.messages[prop];
}
}, this);
},
buildRendering: function(){
this.inherited(arguments);
// Create edit widget in place in the template
// TODO: remove getObject() for 2.0
var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor;
// Copy the style from the source
// Don't copy ALL properties though, just the necessary/applicable ones.
// wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
// is a relative value like 200%, rather than an absolute value like 24px, and
// the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
var srcStyle = this.sourceStyle,
editStyle = "line-height:" + srcStyle.lineHeight + ";",
destStyle = domStyle.getComputedStyle(this.domNode);
array.forEach(["Weight", "Family", "Size", "Style"], function(prop){
var textStyle = srcStyle["font" + prop],
wrapperStyle = destStyle["font" + prop];
if(wrapperStyle != textStyle){
editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";";
}
}, this);
array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){
this.domNode.style[prop] = srcStyle[prop];
}, this);
var width = this.inlineEditBox.width;
if(width == "100%"){
// block mode
editStyle += "width:100%;";
this.domNode.style.display = "block";
}else{
// inline-block mode
editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
}
var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
style: editStyle,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
// set the value in onLoadDeferred instead so the widget has time to finish initializing
//editorParams[("displayedValue" in Cls.prototype || "_setDisplayedValueAttr" in Cls.prototype) ? "displayedValue" : "value"] = this.value;
this.editWidget = new Cls(editorParams, this.editorPlaceholder);
if(this.inlineEditBox.autoSave){
// Remove the save/cancel buttons since saving is done by simply tabbing away or
// selecting a value from the drop down list
this.saveButton.destroy();
this.cancelButton.destroy();
this.saveButton = this.cancelButton = null;
domConstruct.destroy(this.buttonContainer);
}
},
postCreate: function(){
this.inherited(arguments);
var ew = this.editWidget;
if(this.inlineEditBox.autoSave){
this.own(
// Selecting a value from a drop down list causes an onChange event and then we save
aspect.after(ew, "onChange", lang.hitch(this, "_onChange"), true),
// ESC and TAB should cancel and save.
on(ew, "keydown", lang.hitch(this, "_onKeyDown"))
);
}else{
// If possible, enable/disable save button based on whether the user has changed the value
if("intermediateChanges" in ew){
ew.set("intermediateChanges", true);
this.own(aspect.after(ew, "onChange", lang.hitch(this, "_onIntermediateChange"), true));
this.saveButton.set("disabled", true);
}
}
},
startup: function(){
this.editWidget.startup();
this.inherited(arguments);
},
_onIntermediateChange: function(/*===== val =====*/){
// summary:
// Called for editor widgets that support the intermediateChanges=true flag as a way
// to detect when to enable/disabled the save button
this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
},
destroy: function(){
this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
this.inherited(arguments);
},
getValue: function(){
// summary:
// Return the [display] value of the edit widget
var ew = this.editWidget;
return String(ew.get(("displayedValue" in ew || "_getDisplayedValueAttr" in ew) ? "displayedValue" : "value"));
},
_onKeyDown: function(e){
// summary:
// Handler for keydown in the edit box in autoSave mode.
// description:
// For autoSave widgets, if Esc/Enter, call cancel/save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(e.altKey || e.ctrlKey){
return;
}
// If Enter/Esc pressed, treat as save/cancel.
if(e.keyCode == keys.ESCAPE){
e.stopPropagation();
e.preventDefault();
this.cancel(true); // sets editing=false which short-circuits _onBlur processing
}else if(e.keyCode == keys.ENTER && e.target.tagName == "INPUT"){
e.stopPropagation();
e.preventDefault();
this._onChange(); // fire _onBlur and then save
}
// _onBlur will handle TAB automatically by allowing
// the TAB to change focus before we mess with the DOM: #6227
// Expounding by request:
// The current focus is on the edit widget input field.
// save() will hide and destroy this widget.
// We want the focus to jump from the currently hidden
// displayNode, but since it's hidden, it's impossible to
// unhide it, focus it, and then have the browser focus
// away from it to the next focusable element since each
// of these events is asynchronous and the focus-to-next-element
// is already queued.
// So we allow the browser time to unqueue the move-focus event
// before we do all the hide/show stuff.
}
},
_onBlur: function(){
// summary:
// Called when focus moves outside the editor
// tags:
// private
this.inherited(arguments);
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(this.getValue() == this._resetValue){
this.cancel(false);
}else if(this.enableSave()){
this.save(false);
}
}
},
_onChange: function(){
// summary:
// Called when the underlying widget fires an onChange event,
// such as when the user selects a value from the drop down list of a ComboBox,
// which means that the user has finished entering the value and we should save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
}
},
enableSave: function(){
// summary:
// User overridable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to invalid conditions
// tags:
// extension
return this.editWidget.isValid ? this.editWidget.isValid() : true;
},
focus: function(){
// summary:
// Focus the edit widget.
// tags:
// protected
this.editWidget.focus();
if(this.editWidget.focusNode){
// IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
fm._onFocusNode(this.editWidget.focusNode);
if(this.editWidget.focusNode.tagName == "INPUT"){
this.defer(function(){
_TextBoxMixin.selectInputText(this.editWidget.focusNode);
});
}
}
}
});
var InlineEditBox = declare("dijit.InlineEditBox" + (has("dojo-bidi") ? "_NoBidi" : ""), _Widget, {
// summary:
// An element with in-line edit capabilities
//
// description:
// Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
// when you click it, an editor shows up in place of the original
// text. Optionally, Save and Cancel button are displayed below the edit widget.
// When Save is clicked, the text is pulled from the edit
// widget and redisplayed and the edit widget is again hidden.
// By default a plain Textarea widget is used as the editor (or for
// inline values a TextBox), but you can specify an editor such as
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
// An edit widget must support the following API to be used:
//
// - displayedValue or value as initialization parameter,
// and available through set('displayedValue') / set('value')
// - void focus()
// - DOM-node focusNode = node containing editable text
// editing: [readonly] Boolean
// Is the node currently in edit mode?
editing: false,
// autoSave: [const] Boolean
// Changing the value automatically saves it; don't have to push save button
// (and save button isn't even displayed)
autoSave: true,
// buttonSave: String
// Save button label
buttonSave: "",
// buttonCancel: String
// Cancel button label
buttonCancel: "",
// renderAsHtml: Boolean
// Set this to true if the specified Editor's value should be interpreted as HTML
// rather than plain text (ex: `dijit.Editor`)
renderAsHtml: false,
// editor: String|Function
// MID (ex: "dijit/form/TextBox") or constructor for editor widget
editor: TextBox,
// editorWrapper: String|Function
// Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
// buttons.
editorWrapper: InlineEditor,
// editorParams: Object
// Set of parameters for editor, like {required: true}
editorParams: {},
// disabled: Boolean
// If true, clicking the InlineEditBox to edit it will have no effect.
disabled: false,
onChange: function(/*===== value =====*/){
// summary:
// Set this handler to be notified of changes to value.
// tags:
// callback
},
onCancel: function(){
// summary:
// Set this handler to be notified when editing is cancelled.
// tags:
// callback
},
// width: String
// Width of editor. By default it's width=100% (ie, block mode).
width: "100%",
// value: String
// The display value of the widget in read-only mode
value: "",
// noValueIndicator: [const] String
// The text that gets displayed when there is no value (so that the user has a place to click to edit)
noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
"<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
"<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // &#160; == &nbsp;
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my value
// - replace srcNodeRef with my generated DOM tree
this.editorParams = {};
},
postMixInProperties: function(){
this.inherited(arguments);
// save pointer to original source node, since Widget nulls-out srcNodeRef
this.displayNode = this.srcNodeRef;
// connect handlers to the display node
this.own(
on(this.displayNode, a11yclick, lang.hitch(this, "_onClick")),
on(this.displayNode, "mouseover, focus", lang.hitch(this, "_onMouseOver")),
on(this.displayNode, "mouseout, blur", lang.hitch(this, "_onMouseOut"))
);
this.displayNode.setAttribute("role", "button");
if(!this.displayNode.getAttribute("tabIndex")){
this.displayNode.setAttribute("tabIndex", 0);
}
if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
(this.displayNode.innerText || this.displayNode.textContent || ""));
}
if(!this.value){
this.displayNode.innerHTML = this.noValueIndicator;
}
domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ disabled){
// summary:
// Hook to make set("disabled", ...) work.
// Set disabled state of widget.
this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false");
if(disabled){
this.displayNode.removeAttribute("tabIndex");
}else{
this.displayNode.setAttribute("tabIndex", 0);
}
domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
this._set("disabled", disabled);
},
_onMouseOver: function(){
// summary:
// Handler for onmouseover and onfocus event.
// tags:
// private
if(!this.disabled){
domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
}
},
_onMouseOut: function(){
// summary:
// Handler for onmouseout and onblur event.
// tags:
// private
domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
},
_onClick: function(/*Event*/ e){
// summary:
// Handler for onclick event.
// tags:
// private
if(this.disabled){
return;
}
if(e){
e.stopPropagation();
e.preventDefault();
}
this._onMouseOut();
// Since FF gets upset if you move a node while in an event handler for that node...
this.defer("edit");
},
edit: function(){
// summary:
// Display the editor widget in place of the original (read only) markup.
// tags:
// private
if(this.disabled || this.editing){
return;
}
this._set('editing', true);
// save some display node values that can be restored later
this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
if(!this.wrapperWidget){
// Placeholder for edit widget
// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
// when Calendar dropdown appears, which happens automatically on focus.
var placeholder = domConstruct.create("span", null, this.domNode, "before");
// Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
this.wrapperWidget = new Ewc({
value: this.value,
buttonSave: this.buttonSave,
buttonCancel: this.buttonCancel,
dir: this.dir,
lang: this.lang,
tabIndex: this._savedTabIndex,
editor: this.editor,
inlineEditBox: this,
sourceStyle: domStyle.getComputedStyle(this.displayNode),
save: lang.hitch(this, "save"),
cancel: lang.hitch(this, "cancel"),
textDir: this.textDir
}, placeholder);
if(!this.wrapperWidget._started){
this.wrapperWidget.startup();
}
if(!this._started){
this.startup();
}
}
var ww = this.wrapperWidget;
// to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
// and then when it's finished rendering, we switch from display mode to editor
// position: absolute releases screen space allocated to the display node
// opacity:0 is the same as visibility: hidden but is still focusable
// visibility: hidden removes focus outline
domClass.add(this.displayNode, "dijitOffScreen");
domClass.remove(ww.domNode, "dijitOffScreen");
domStyle.set(ww.domNode, { visibility: "visible" });
domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
// After edit widget has finished initializing (in particular need to wait for dijit.Editor),
// or immediately if there is no onLoadDeferred Deferred,
// replace the display widget with edit widget, leaving them both displayed for a brief time so that
// focus can be shifted without incident.
var ew = ww.editWidget;
var self = this;
when(ew.onLoadDeferred, lang.hitch(ww, function(){
// set value again in case the edit widget's value is just now valid
ew.set(("displayedValue" in ew || "_setDisplayedValueAttr" in ew) ? "displayedValue" : "value", self.value);
this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
// the saveButton should start out disabled in most cases but the above set could have fired onChange
if(ww.saveButton){
ww.saveButton.set("disabled", "intermediateChanges" in ew);
}
this.focus(); // both nodes are showing, so we can switch focus safely
this._resetValue = this.getValue();
});
}));
},
_onBlur: function(){
// summary:
// Called when focus moves outside the InlineEditBox.
// Performs garbage collection.
// tags:
// private
this.inherited(arguments);
if(!this.editing){
/* causes IE focus problems, see TooltipDialog_a11y.html...
this.defer(function(){
if(this.wrapperWidget){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
});
*/
}
},
destroy: function(){
if(this.wrapperWidget && !this.wrapperWidget._destroyed){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
this.inherited(arguments);
},
_showText: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, and optionally focus on display node
// tags:
// private
var ww = this.wrapperWidget;
domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events
domClass.add(ww.domNode, "dijitOffScreen");
domClass.remove(this.displayNode, "dijitOffScreen");
domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
if(focus){
fm.focus(this.displayNode);
}
},
save: function(/*Boolean*/ focus){
// summary:
// Save the contents of the editor and revert to display mode.
// focus: Boolean
// Focus on the display mode text
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
var ww = this.wrapperWidget;
var value = ww.getValue();
this.set('value', value); // display changed, formatted value
this._showText(focus); // set focus as needed
},
setValue: function(/*String*/ val){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
return this.set("value", val);
},
_setValueAttr: function(/*String*/ val){
// summary:
// Hook to make set("value", ...) work.
// Inserts specified HTML value into this node, or an "input needed" character if node is blank.
val = lang.trim(val);
var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
this.displayNode.innerHTML = renderVal || this.noValueIndicator;
this._set("value", val);
if(this._started){
// tell the world that we have changed
this.defer(function(){
this.onChange(val);
}); // defer prevents browser freeze for long-running event handlers
}
},
getValue: function(){
// summary:
// Deprecated. Use get('value') instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
return this.get("value");
},
cancel: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, discarding any changes made in the editor
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
// tell the world that we have no changes
this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers
this._showText(focus);
}
});
if(has("dojo-bidi")){
InlineEditBox = declare("dijit.InlineEditBox", InlineEditBox, {
_setValueAttr: function(){
this.inherited(arguments);
this.applyTextDir(this.displayNode);
}
});
}
InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
return InlineEditBox;
});
@@ -1,660 +0,0 @@
require({cache:{
'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"./form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"./form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
define("dijit/InlineEditBox", [
"require",
"dojo/_base/array", // array.forEach
"dojo/aspect",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set domAttr.get
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
"dojo/i18n", // i18n.getLocalization
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.getObject
"dojo/on",
"dojo/sniff", // has("ie")
"dojo/when",
"./a11yclick",
"./focus",
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"./_Container",
"./form/Button",
"./form/_TextBoxMixin",
"./form/TextBox",
"dojo/text!./templates/InlineEditBox.html",
"dojo/i18n!./nls/common"
], function(require, array, aspect, declare, domAttr, domClass, domConstruct, domStyle, i18n, kernel, keys, lang, on, has, when, a11yclick, fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
// module:
// dijit/InlineEditBox
var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// Internal widget used by InlineEditBox, displayed when in editing mode
// to display the editor and maybe save/cancel buttons. Calling code should
// connect to save/cancel methods to detect when editing is finished
//
// Has mainly the same parameters as InlineEditBox, plus these values:
//
// style: Object
// Set of CSS attributes of display node, to replicate in editor
//
// value: String
// Value as an HTML string or plain text string, depending on renderAsHTML flag
templateString: template,
contextRequire: require,
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit", "common", this.lang);
array.forEach(["buttonSave", "buttonCancel"], function(prop){
if(!this[prop]){
this[prop] = this.messages[prop];
}
}, this);
},
buildRendering: function(){
this.inherited(arguments);
// Create edit widget in place in the template
// TODO: remove getObject() for 2.0
var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor;
// Copy the style from the source
// Don't copy ALL properties though, just the necessary/applicable ones.
// wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
// is a relative value like 200%, rather than an absolute value like 24px, and
// the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
var srcStyle = this.sourceStyle,
editStyle = "line-height:" + srcStyle.lineHeight + ";",
destStyle = domStyle.getComputedStyle(this.domNode);
array.forEach(["Weight", "Family", "Size", "Style"], function(prop){
var textStyle = srcStyle["font" + prop],
wrapperStyle = destStyle["font" + prop];
if(wrapperStyle != textStyle){
editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";";
}
}, this);
array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){
this.domNode.style[prop] = srcStyle[prop];
}, this);
var width = this.inlineEditBox.width;
if(width == "100%"){
// block mode
editStyle += "width:100%;";
this.domNode.style.display = "block";
}else{
// inline-block mode
editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
}
var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
style: editStyle,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
// set the value in onLoadDeferred instead so the widget has time to finish initializing
//editorParams[("displayedValue" in Cls.prototype || "_setDisplayedValueAttr" in Cls.prototype) ? "displayedValue" : "value"] = this.value;
this.editWidget = new Cls(editorParams, this.editorPlaceholder);
if(this.inlineEditBox.autoSave){
// Remove the save/cancel buttons since saving is done by simply tabbing away or
// selecting a value from the drop down list
this.saveButton.destroy();
this.cancelButton.destroy();
this.saveButton = this.cancelButton = null;
domConstruct.destroy(this.buttonContainer);
}
},
postCreate: function(){
this.inherited(arguments);
var ew = this.editWidget;
if(this.inlineEditBox.autoSave){
this.own(
// Selecting a value from a drop down list causes an onChange event and then we save
aspect.after(ew, "onChange", lang.hitch(this, "_onChange"), true),
// ESC and TAB should cancel and save.
on(ew, "keydown", lang.hitch(this, "_onKeyDown"))
);
}else{
// If possible, enable/disable save button based on whether the user has changed the value
if("intermediateChanges" in ew){
ew.set("intermediateChanges", true);
this.own(aspect.after(ew, "onChange", lang.hitch(this, "_onIntermediateChange"), true));
this.saveButton.set("disabled", true);
}
}
},
startup: function(){
this.editWidget.startup();
this.inherited(arguments);
},
_onIntermediateChange: function(/*===== val =====*/){
// summary:
// Called for editor widgets that support the intermediateChanges=true flag as a way
// to detect when to enable/disabled the save button
this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
},
destroy: function(){
this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
this.inherited(arguments);
},
getValue: function(){
// summary:
// Return the [display] value of the edit widget
var ew = this.editWidget;
return String(ew.get(("displayedValue" in ew || "_getDisplayedValueAttr" in ew) ? "displayedValue" : "value"));
},
_onKeyDown: function(e){
// summary:
// Handler for keydown in the edit box in autoSave mode.
// description:
// For autoSave widgets, if Esc/Enter, call cancel/save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(e.altKey || e.ctrlKey){
return;
}
// If Enter/Esc pressed, treat as save/cancel.
if(e.keyCode == keys.ESCAPE){
e.stopPropagation();
e.preventDefault();
this.cancel(true); // sets editing=false which short-circuits _onBlur processing
}else if(e.keyCode == keys.ENTER && e.target.tagName == "INPUT"){
e.stopPropagation();
e.preventDefault();
this._onChange(); // fire _onBlur and then save
}
// _onBlur will handle TAB automatically by allowing
// the TAB to change focus before we mess with the DOM: #6227
// Expounding by request:
// The current focus is on the edit widget input field.
// save() will hide and destroy this widget.
// We want the focus to jump from the currently hidden
// displayNode, but since it's hidden, it's impossible to
// unhide it, focus it, and then have the browser focus
// away from it to the next focusable element since each
// of these events is asynchronous and the focus-to-next-element
// is already queued.
// So we allow the browser time to unqueue the move-focus event
// before we do all the hide/show stuff.
}
},
_onBlur: function(){
// summary:
// Called when focus moves outside the editor
// tags:
// private
this.inherited(arguments);
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(this.getValue() == this._resetValue){
this.cancel(false);
}else if(this.enableSave()){
this.save(false);
}
}
},
_onChange: function(){
// summary:
// Called when the underlying widget fires an onChange event,
// such as when the user selects a value from the drop down list of a ComboBox,
// which means that the user has finished entering the value and we should save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
}
},
enableSave: function(){
// summary:
// User overridable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to invalid conditions
// tags:
// extension
return this.editWidget.isValid ? this.editWidget.isValid() : true;
},
focus: function(){
// summary:
// Focus the edit widget.
// tags:
// protected
this.editWidget.focus();
if(this.editWidget.focusNode){
// IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
fm._onFocusNode(this.editWidget.focusNode);
if(this.editWidget.focusNode.tagName == "INPUT"){
this.defer(function(){
_TextBoxMixin.selectInputText(this.editWidget.focusNode);
});
}
}
}
});
var InlineEditBox = declare("dijit.InlineEditBox" + (has("dojo-bidi") ? "_NoBidi" : ""), _Widget, {
// summary:
// An element with in-line edit capabilities
//
// description:
// Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
// when you click it, an editor shows up in place of the original
// text. Optionally, Save and Cancel button are displayed below the edit widget.
// When Save is clicked, the text is pulled from the edit
// widget and redisplayed and the edit widget is again hidden.
// By default a plain Textarea widget is used as the editor (or for
// inline values a TextBox), but you can specify an editor such as
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
// An edit widget must support the following API to be used:
//
// - displayedValue or value as initialization parameter,
// and available through set('displayedValue') / set('value')
// - void focus()
// - DOM-node focusNode = node containing editable text
// editing: [readonly] Boolean
// Is the node currently in edit mode?
editing: false,
// autoSave: [const] Boolean
// Changing the value automatically saves it; don't have to push save button
// (and save button isn't even displayed)
autoSave: true,
// buttonSave: String
// Save button label
buttonSave: "",
// buttonCancel: String
// Cancel button label
buttonCancel: "",
// renderAsHtml: Boolean
// Set this to true if the specified Editor's value should be interpreted as HTML
// rather than plain text (ex: `dijit.Editor`)
renderAsHtml: false,
// editor: String|Function
// MID (ex: "dijit/form/TextBox") or constructor for editor widget
editor: TextBox,
// editorWrapper: String|Function
// Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
// buttons.
editorWrapper: InlineEditor,
// editorParams: Object
// Set of parameters for editor, like {required: true}
editorParams: {},
// disabled: Boolean
// If true, clicking the InlineEditBox to edit it will have no effect.
disabled: false,
onChange: function(/*===== value =====*/){
// summary:
// Set this handler to be notified of changes to value.
// tags:
// callback
},
onCancel: function(){
// summary:
// Set this handler to be notified when editing is cancelled.
// tags:
// callback
},
// width: String
// Width of editor. By default it's width=100% (ie, block mode).
width: "100%",
// value: String
// The display value of the widget in read-only mode
value: "",
// noValueIndicator: [const] String
// The text that gets displayed when there is no value (so that the user has a place to click to edit)
noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
"<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
"<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // &#160; == &nbsp;
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my value
// - replace srcNodeRef with my generated DOM tree
this.editorParams = {};
},
postMixInProperties: function(){
this.inherited(arguments);
// save pointer to original source node, since Widget nulls-out srcNodeRef
this.displayNode = this.srcNodeRef;
// connect handlers to the display node
this.own(
on(this.displayNode, a11yclick, lang.hitch(this, "_onClick")),
on(this.displayNode, "mouseover, focus", lang.hitch(this, "_onMouseOver")),
on(this.displayNode, "mouseout, blur", lang.hitch(this, "_onMouseOut"))
);
this.displayNode.setAttribute("role", "button");
if(!this.displayNode.getAttribute("tabIndex")){
this.displayNode.setAttribute("tabIndex", 0);
}
if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
(this.displayNode.innerText || this.displayNode.textContent || ""));
}
if(!this.value){
this.displayNode.innerHTML = this.noValueIndicator;
}
domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ disabled){
// summary:
// Hook to make set("disabled", ...) work.
// Set disabled state of widget.
this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false");
if(disabled){
this.displayNode.removeAttribute("tabIndex");
}else{
this.displayNode.setAttribute("tabIndex", 0);
}
domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
this._set("disabled", disabled);
},
_onMouseOver: function(){
// summary:
// Handler for onmouseover and onfocus event.
// tags:
// private
if(!this.disabled){
domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
}
},
_onMouseOut: function(){
// summary:
// Handler for onmouseout and onblur event.
// tags:
// private
domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
},
_onClick: function(/*Event*/ e){
// summary:
// Handler for onclick event.
// tags:
// private
if(this.disabled){
return;
}
if(e){
e.stopPropagation();
e.preventDefault();
}
this._onMouseOut();
// Since FF gets upset if you move a node while in an event handler for that node...
this.defer("edit");
},
edit: function(){
// summary:
// Display the editor widget in place of the original (read only) markup.
// tags:
// private
if(this.disabled || this.editing){
return;
}
this._set('editing', true);
// save some display node values that can be restored later
this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
if(!this.wrapperWidget){
// Placeholder for edit widget
// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
// when Calendar dropdown appears, which happens automatically on focus.
var placeholder = domConstruct.create("span", null, this.domNode, "before");
// Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
this.wrapperWidget = new Ewc({
value: this.value,
buttonSave: this.buttonSave,
buttonCancel: this.buttonCancel,
dir: this.dir,
lang: this.lang,
tabIndex: this._savedTabIndex,
editor: this.editor,
inlineEditBox: this,
sourceStyle: domStyle.getComputedStyle(this.displayNode),
save: lang.hitch(this, "save"),
cancel: lang.hitch(this, "cancel"),
textDir: this.textDir
}, placeholder);
if(!this.wrapperWidget._started){
this.wrapperWidget.startup();
}
if(!this._started){
this.startup();
}
}
var ww = this.wrapperWidget;
// to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
// and then when it's finished rendering, we switch from display mode to editor
// position: absolute releases screen space allocated to the display node
// opacity:0 is the same as visibility: hidden but is still focusable
// visibility: hidden removes focus outline
domClass.add(this.displayNode, "dijitOffScreen");
domClass.remove(ww.domNode, "dijitOffScreen");
domStyle.set(ww.domNode, { visibility: "visible" });
domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
// After edit widget has finished initializing (in particular need to wait for dijit.Editor),
// or immediately if there is no onLoadDeferred Deferred,
// replace the display widget with edit widget, leaving them both displayed for a brief time so that
// focus can be shifted without incident.
var ew = ww.editWidget;
var self = this;
when(ew.onLoadDeferred, lang.hitch(ww, function(){
// set value again in case the edit widget's value is just now valid
ew.set(("displayedValue" in ew || "_setDisplayedValueAttr" in ew) ? "displayedValue" : "value", self.value);
this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
// the saveButton should start out disabled in most cases but the above set could have fired onChange
if(ww.saveButton){
ww.saveButton.set("disabled", "intermediateChanges" in ew);
}
this.focus(); // both nodes are showing, so we can switch focus safely
this._resetValue = this.getValue();
});
}));
},
_onBlur: function(){
// summary:
// Called when focus moves outside the InlineEditBox.
// Performs garbage collection.
// tags:
// private
this.inherited(arguments);
if(!this.editing){
/* causes IE focus problems, see TooltipDialog_a11y.html...
this.defer(function(){
if(this.wrapperWidget){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
});
*/
}
},
destroy: function(){
if(this.wrapperWidget && !this.wrapperWidget._destroyed){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
this.inherited(arguments);
},
_showText: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, and optionally focus on display node
// tags:
// private
var ww = this.wrapperWidget;
domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events
domClass.add(ww.domNode, "dijitOffScreen");
domClass.remove(this.displayNode, "dijitOffScreen");
domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
if(focus){
fm.focus(this.displayNode);
}
},
save: function(/*Boolean*/ focus){
// summary:
// Save the contents of the editor and revert to display mode.
// focus: Boolean
// Focus on the display mode text
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
var ww = this.wrapperWidget;
var value = ww.getValue();
this.set('value', value); // display changed, formatted value
this._showText(focus); // set focus as needed
},
setValue: function(/*String*/ val){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
return this.set("value", val);
},
_setValueAttr: function(/*String*/ val){
// summary:
// Hook to make set("value", ...) work.
// Inserts specified HTML value into this node, or an "input needed" character if node is blank.
val = lang.trim(val);
var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
this.displayNode.innerHTML = renderVal || this.noValueIndicator;
this._set("value", val);
if(this._started){
// tell the world that we have changed
this.defer(function(){
this.onChange(val);
}); // defer prevents browser freeze for long-running event handlers
}
},
getValue: function(){
// summary:
// Deprecated. Use get('value') instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
return this.get("value");
},
cancel: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, discarding any changes made in the editor
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
// tell the world that we have no changes
this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers
this._showText(focus);
}
});
if(has("dojo-bidi")){
InlineEditBox = declare("dijit.InlineEditBox", InlineEditBox, {
_setValueAttr: function(){
this.inherited(arguments);
this.applyTextDir(this.displayNode);
}
});
}
InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
return InlineEditBox;
});
@@ -1,143 +1,386 @@
//>>built
define("dijit/Menu",["require","dojo/_base/array","dojo/_base/declare","dojo/dom","dojo/dom-attr","dojo/dom-geometry","dojo/dom-style","dojo/keys","dojo/_base/lang","dojo/on","dojo/sniff","dojo/_base/window","dojo/window","./popup","./DropDownMenu","dojo/ready"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,on,_a,_b,_c,pm,_d,_e){
if(_a("dijit-legacy-requires")){
_e(0,function(){
var _f=["dijit/MenuItem","dijit/PopupMenuItem","dijit/CheckedMenuItem","dijit/MenuSeparator"];
_1(_f);
});
}
return _3("dijit.Menu",_d,{constructor:function(){
this._bindings=[];
},targetNodeIds:[],selector:"",contextMenuForWindow:false,leftClickToOpen:false,refocus:true,postCreate:function(){
if(this.contextMenuForWindow){
this.bindDomNode(this.ownerDocumentBody);
}else{
_2.forEach(this.targetNodeIds,this.bindDomNode,this);
}
this.inherited(arguments);
},_iframeContentWindow:function(_10){
return _c.get(this._iframeContentDocument(_10))||this._iframeContentDocument(_10)["__parent__"]||(_10.name&&document.frames[_10.name])||null;
},_iframeContentDocument:function(_11){
return _11.contentDocument||(_11.contentWindow&&_11.contentWindow.document)||(_11.name&&document.frames[_11.name]&&document.frames[_11.name].document)||null;
},bindDomNode:function(_12){
_12=_4.byId(_12,this.ownerDocument);
var cn;
if(_12.tagName.toLowerCase()=="iframe"){
var _13=_12,_14=this._iframeContentWindow(_13);
cn=_b.body(_14.document);
}else{
cn=(_12==_b.body(this.ownerDocument)?this.ownerDocument.documentElement:_12);
}
var _15={node:_12,iframe:_13};
_5.set(_12,"_dijitMenu"+this.id,this._bindings.push(_15));
var _16=_9.hitch(this,function(cn){
var _17=this.selector,_18=_17?function(_19){
return on.selector(_17,_19);
}:function(_1a){
return _1a;
},_1b=this;
return [on(cn,_18(this.leftClickToOpen?"click":"contextmenu"),function(evt){
evt.stopPropagation();
evt.preventDefault();
if((new Date()).getTime()<this._lastKeyDown+500){
return;
}
_1b._scheduleOpen(this,_13,{x:evt.pageX,y:evt.pageY},evt.target);
}),on(cn,_18("keydown"),function(evt){
if(evt.keyCode==93||(evt.shiftKey&&evt.keyCode==_8.F10)||(this.leftClickToOpen&&evt.keyCode==_8.SPACE)){
evt.stopPropagation();
evt.preventDefault();
_1b._scheduleOpen(this,_13,null,evt.target);
this._lastKeyDown=(new Date()).getTime();
}
})];
});
_15.connects=cn?_16(cn):[];
if(_13){
_15.onloadHandler=_9.hitch(this,function(){
var _1c=this._iframeContentWindow(_13),cn=_b.body(_1c.document);
_15.connects=_16(cn);
});
if(_13.addEventListener){
_13.addEventListener("load",_15.onloadHandler,false);
}else{
_13.attachEvent("onload",_15.onloadHandler);
}
}
},unBindDomNode:function(_1d){
var _1e;
try{
_1e=_4.byId(_1d,this.ownerDocument);
}
catch(e){
return;
}
var _1f="_dijitMenu"+this.id;
if(_1e&&_5.has(_1e,_1f)){
var bid=_5.get(_1e,_1f)-1,b=this._bindings[bid],h;
while((h=b.connects.pop())){
h.remove();
}
var _20=b.iframe;
if(_20){
if(_20.removeEventListener){
_20.removeEventListener("load",b.onloadHandler,false);
}else{
_20.detachEvent("onload",b.onloadHandler);
}
}
_5.remove(_1e,_1f);
delete this._bindings[bid];
}
},_scheduleOpen:function(_21,_22,_23,_24){
if(!this._openTimer){
this._openTimer=this.defer(function(){
delete this._openTimer;
this._openMyself({target:_24,delegatedTarget:_21,iframe:_22,coords:_23});
},1);
}
},_openMyself:function(_25){
var _26=_25.target,_27=_25.iframe,_28=_25.coords,_29=!_28;
this.currentTarget=_25.delegatedTarget;
if(_28){
if(_27){
var ifc=_6.position(_27,true),_2a=this._iframeContentWindow(_27),_2b=_6.docScroll(_2a.document);
var cs=_7.getComputedStyle(_27),tp=_7.toPixelValue,_2c=(_a("ie")&&_a("quirks")?0:tp(_27,cs.paddingLeft))+(_a("ie")&&_a("quirks")?tp(_27,cs.borderLeftWidth):0),top=(_a("ie")&&_a("quirks")?0:tp(_27,cs.paddingTop))+(_a("ie")&&_a("quirks")?tp(_27,cs.borderTopWidth):0);
_28.x+=ifc.x+_2c-_2b.x;
_28.y+=ifc.y+top-_2b.y;
}
}else{
_28=_6.position(_26,true);
_28.x+=10;
_28.y+=10;
}
var _2d=this;
var _2e=this._focusManager.get("prevNode");
var _2f=this._focusManager.get("curNode");
var _30=!_2f||(_4.isDescendant(_2f,this.domNode))?_2e:_2f;
function _31(){
if(_2d.refocus&&_30){
_30.focus();
}
pm.close(_2d);
};
pm.open({popup:this,x:_28.x,y:_28.y,onExecute:_31,onCancel:_31,orient:this.isLeftToRight()?"L":"R"});
this.focus();
if(!_29){
this.defer(function(){
this._cleanUp(true);
});
}
this._onBlur=function(){
this.inherited("_onBlur",arguments);
pm.close(this);
};
},destroy:function(){
_2.forEach(this._bindings,function(b){
if(b){
this.unBindDomNode(b.node);
}
},this);
this.inherited(arguments);
}});
define([
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId dom.isDescendant
"dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
"dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/keys", // keys.F10
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie"), has("quirks")
"dojo/_base/window", // win.body
"dojo/window", // winUtils.get
"./popup",
"./DropDownMenu",
"dojo/ready"
], function(require, array, declare, dom, domAttr, domGeometry, domStyle, keys, lang, on, has, win, winUtils, pm, DropDownMenu, ready){
// module:
// dijit/Menu
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Menu", DropDownMenu, {
// summary:
// A context menu you can assign to multiple elements
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my contents
// - replace srcNodeRef with my generated DOM tree
this._bindings = [];
},
// targetNodeIds: [const] String[]
// Array of dom node ids of nodes to attach to.
// Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
targetNodeIds: [],
// selector: String?
// CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
// the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
// a range of rows in a table, tree, etc.
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
// So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
/*=====
// currentTarget: [readonly] DOMNode
// For context menus, set to the current node that the Menu is being displayed for.
// Useful so that the menu actions can be tailored according to the node
currentTarget: null,
=====*/
// contextMenuForWindow: [const] Boolean
// If true, right clicking anywhere on the window will cause this context menu to open.
// If false, must specify targetNodeIds.
contextMenuForWindow: false,
// leftClickToOpen: [const] Boolean
// If true, menu will open on left click instead of right click, similar to a file menu.
leftClickToOpen: false,
// TODO: remove in 2.0, we have better ways of opening a menu with a left click, by extending _HasDropDown.
// refocus: Boolean
// When this menu closes, re-focus the element which had focus before it was opened.
refocus: true,
postCreate: function(){
if(this.contextMenuForWindow){
this.bindDomNode(this.ownerDocumentBody);
}else{
array.forEach(this.targetNodeIds, this.bindDomNode, this);
}
this.inherited(arguments);
},
// thanks burstlib!
_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns the window reference of the passed iframe
// tags:
// private
return winUtils.get(this._iframeContentDocument(iframe_el)) ||
// Moz. TODO: is this available when defaultView isn't?
this._iframeContentDocument(iframe_el)['__parent__'] ||
(iframe_el.name && document.frames[iframe_el.name]) || null; // Window
},
_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns a reference to the document object inside iframe_el
// tags:
// protected
return iframe_el.contentDocument // W3
|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
|| (iframe_el.name && document.frames[iframe_el.name] && document.frames[iframe_el.name].document)
|| null; // HTMLDocument
},
bindDomNode: function(/*String|DomNode*/ node){
// summary:
// Attach menu to given node
node = dom.byId(node, this.ownerDocument);
var cn; // Connect node
// Support context menus on iframes. Rather than binding to the iframe itself we need
// to bind to the <body> node inside the iframe.
if(node.tagName.toLowerCase() == "iframe"){
var iframe = node,
window = this._iframeContentWindow(iframe);
cn = win.body(window.document);
}else{
// To capture these events at the top level, attach to <html>, not <body>.
// Otherwise right-click context menu just doesn't work.
cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node);
}
// "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
var binding = {
node: node,
iframe: iframe
};
// Save info about binding in _bindings[], and make node itself record index(+1) into
// _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
// start with a number, which fails on FF/safari.
domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
// Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
// loading yet, in which case we need to wait for the onload event first, and then connect
// On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
// we need to monitor keyboard events in addition to the oncontextmenu event.
var doConnects = lang.hitch(this, function(cn){
var selector = this.selector,
delegatedEvent = selector ?
function(eventType){
return on.selector(selector, eventType);
} :
function(eventType){
return eventType;
},
self = this;
return [
on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){
evt.stopPropagation();
evt.preventDefault();
if((new Date()).getTime() < this._lastKeyDown + 500){
// Ignore contextmenu/click events that were already processed in keydown handler below.
// But still call preventDefault() (above) so system context menu doesn't appear.
return;
}
// Schedule context menu to be opened.
// Note that this won't work will if the click was generated by the keyboard, while
// focused on a <button> etc. In that case evt.pageX and evt.pageY are either (0,0) or
// wherever the mouse cursor is. See keydown handler below.
self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY}, evt.target);
}),
on(cn, delegatedEvent("keydown"), function(evt){
if(evt.keyCode == 93 || // context menu key
(evt.shiftKey && evt.keyCode == keys.F10) || // shift-F10
(this.leftClickToOpen && evt.keyCode == keys.SPACE) // space key
){
evt.stopPropagation();
evt.preventDefault();
// Open the menu around evt.target. Note that "this" and evt.target
// are likely different, especially for global context menu, where "this" is <body>.
self._scheduleOpen(this, iframe, null, evt.target); // no coords - open near evt.target
this._lastKeyDown = (new Date()).getTime();
}
})
];
});
binding.connects = cn ? doConnects(cn) : [];
if(iframe){
// Setup handler to [re]bind to the iframe when the contents are initially loaded,
// and every time the contents change.
// Need to do this b/c we are actually binding to the iframe's <body> node.
// Note: can't use connect.connect(), see #9609.
binding.onloadHandler = lang.hitch(this, function(){
// want to remove old connections, but IE throws exceptions when trying to
// access the <body> node because it's already gone, or at least in a state of limbo
var window = this._iframeContentWindow(iframe),
cn = win.body(window.document);
binding.connects = doConnects(cn);
});
if(iframe.addEventListener){
iframe.addEventListener("load", binding.onloadHandler, false);
}else{
iframe.attachEvent("onload", binding.onloadHandler);
}
}
},
unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
// Detach menu from given node
var node;
try{
node = dom.byId(nodeName, this.ownerDocument);
}catch(e){
// On IE the dom.byId() call will get an exception if the attach point was
// the <body> node of an <iframe> that has since been reloaded (and thus the
// <body> node is in a limbo state of destruction.
return;
}
// node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
var attrName = "_dijitMenu" + this.id;
if(node && domAttr.has(node, attrName)){
var bid = domAttr.get(node, attrName) - 1, b = this._bindings[bid], h;
while((h = b.connects.pop())){
h.remove();
}
// Remove listener for iframe onload events
var iframe = b.iframe;
if(iframe){
if(iframe.removeEventListener){
iframe.removeEventListener("load", b.onloadHandler, false);
}else{
iframe.detachEvent("onload", b.onloadHandler);
}
}
domAttr.remove(node, attrName);
delete this._bindings[bid];
}
},
_scheduleOpen: function(delegatedTarget, iframe, coords, target){
// summary:
// Set timer to display myself. Using a timer rather than displaying immediately solves
// IE problem: without the delay, focus work in "open" causes the system
// context menu to appear in spite of evt.preventDefault().
// delegatedTarget: Element
// The node specified in targetNodeIds or matching selector that the menu is being opened for.
// iframe: HTMLIframeElement?
// Set if target is inside the specified iframe.
// coords: Object
// x/y position to center the menu around. Undefined if menu was opened via keyboard.
// target: Element
// The actual clicked node, either delegatedTarget or a descendant.
if(!this._openTimer){
this._openTimer = this.defer(function(){
delete this._openTimer;
this._openMyself({
target: target,
delegatedTarget: delegatedTarget,
iframe: iframe,
coords: coords
});
}, 1);
}
},
_openMyself: function(args){
// summary:
// Internal function for opening myself when the user does a right-click or something similar.
// args:
// This is an Object containing:
//
// - target: The node that is being clicked.
// - delegatedTarget: The node from this.targetNodeIds or matching this.selector,
// either the same as target or an ancestor of target.
// - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
// - coords: Mouse cursor x/y coordinates. Null when opened via keyboard.
// Put menu at specified position in iframe (if iframe specified) or otherwise in viewport.
//
// _openMyself() formerly took the event object, and since various code references
// evt.target (after connecting to _openMyself()), using an Object for parameters
// (so that old code still works).
var target = args.target,
iframe = args.iframe,
coords = args.coords,
byKeyboard = !coords;
// To be used by MenuItem event handlers to tell which node the menu was opened on
this.currentTarget = args.delegatedTarget;
// Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
// then near the node the menu is assigned to.
if(coords){
if(iframe){
// Specified coordinates are on <body> node of an <iframe>, convert to match main document
var ifc = domGeometry.position(iframe, true),
window = this._iframeContentWindow(iframe),
scroll = domGeometry.docScroll(window.document);
var cs = domStyle.getComputedStyle(iframe),
tp = domStyle.toPixelValue,
left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
coords.x += ifc.x + left - scroll.x;
coords.y += ifc.y + top - scroll.y;
}
}else{
coords = domGeometry.position(target, true);
coords.x += 10;
coords.y += 10;
}
var self = this;
var prevFocusNode = this._focusManager.get("prevNode");
var curFocusNode = this._focusManager.get("curNode");
var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
function closeAndRestoreFocus(){
// user has clicked on a menu or popup
if(self.refocus && savedFocusNode){
savedFocusNode.focus();
}
pm.close(self);
}
pm.open({
popup: this,
x: coords.x,
y: coords.y,
onExecute: closeAndRestoreFocus,
onCancel: closeAndRestoreFocus,
orient: this.isLeftToRight() ? 'L' : 'R'
});
// Focus the menu even when opened by mouse, so that a click on blank area of screen will close it
this.focus();
if(!byKeyboard){
// But then (when opened by mouse), mark Menu as passive, so that the first item isn't highlighted.
// On IE9+ this needs to be on a delay because the focus is asynchronous.
this.defer(function(){
this._cleanUp(true);
});
}
this._onBlur = function(){
this.inherited('_onBlur', arguments);
// Usually the parent closes the child widget but if this is a context
// menu then there is no parent
pm.close(this);
// don't try to restore focus; user has clicked another part of the screen
// and set focus there
};
},
destroy: function(){
array.forEach(this._bindings, function(b){
if(b){
this.unBindDomNode(b.node);
}
}, this);
this.inherited(arguments);
}
});
});
@@ -1,386 +0,0 @@
define("dijit/Menu", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId dom.isDescendant
"dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
"dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/keys", // keys.F10
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie"), has("quirks")
"dojo/_base/window", // win.body
"dojo/window", // winUtils.get
"./popup",
"./DropDownMenu",
"dojo/ready"
], function(require, array, declare, dom, domAttr, domGeometry, domStyle, keys, lang, on, has, win, winUtils, pm, DropDownMenu, ready){
// module:
// dijit/Menu
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Menu", DropDownMenu, {
// summary:
// A context menu you can assign to multiple elements
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my contents
// - replace srcNodeRef with my generated DOM tree
this._bindings = [];
},
// targetNodeIds: [const] String[]
// Array of dom node ids of nodes to attach to.
// Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
targetNodeIds: [],
// selector: String?
// CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
// the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
// a range of rows in a table, tree, etc.
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
// So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
/*=====
// currentTarget: [readonly] DOMNode
// For context menus, set to the current node that the Menu is being displayed for.
// Useful so that the menu actions can be tailored according to the node
currentTarget: null,
=====*/
// contextMenuForWindow: [const] Boolean
// If true, right clicking anywhere on the window will cause this context menu to open.
// If false, must specify targetNodeIds.
contextMenuForWindow: false,
// leftClickToOpen: [const] Boolean
// If true, menu will open on left click instead of right click, similar to a file menu.
leftClickToOpen: false,
// TODO: remove in 2.0, we have better ways of opening a menu with a left click, by extending _HasDropDown.
// refocus: Boolean
// When this menu closes, re-focus the element which had focus before it was opened.
refocus: true,
postCreate: function(){
if(this.contextMenuForWindow){
this.bindDomNode(this.ownerDocumentBody);
}else{
array.forEach(this.targetNodeIds, this.bindDomNode, this);
}
this.inherited(arguments);
},
// thanks burstlib!
_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns the window reference of the passed iframe
// tags:
// private
return winUtils.get(this._iframeContentDocument(iframe_el)) ||
// Moz. TODO: is this available when defaultView isn't?
this._iframeContentDocument(iframe_el)['__parent__'] ||
(iframe_el.name && document.frames[iframe_el.name]) || null; // Window
},
_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns a reference to the document object inside iframe_el
// tags:
// protected
return iframe_el.contentDocument // W3
|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
|| (iframe_el.name && document.frames[iframe_el.name] && document.frames[iframe_el.name].document)
|| null; // HTMLDocument
},
bindDomNode: function(/*String|DomNode*/ node){
// summary:
// Attach menu to given node
node = dom.byId(node, this.ownerDocument);
var cn; // Connect node
// Support context menus on iframes. Rather than binding to the iframe itself we need
// to bind to the <body> node inside the iframe.
if(node.tagName.toLowerCase() == "iframe"){
var iframe = node,
window = this._iframeContentWindow(iframe);
cn = win.body(window.document);
}else{
// To capture these events at the top level, attach to <html>, not <body>.
// Otherwise right-click context menu just doesn't work.
cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node);
}
// "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
var binding = {
node: node,
iframe: iframe
};
// Save info about binding in _bindings[], and make node itself record index(+1) into
// _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
// start with a number, which fails on FF/safari.
domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
// Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
// loading yet, in which case we need to wait for the onload event first, and then connect
// On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
// we need to monitor keyboard events in addition to the oncontextmenu event.
var doConnects = lang.hitch(this, function(cn){
var selector = this.selector,
delegatedEvent = selector ?
function(eventType){
return on.selector(selector, eventType);
} :
function(eventType){
return eventType;
},
self = this;
return [
on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){
evt.stopPropagation();
evt.preventDefault();
if((new Date()).getTime() < this._lastKeyDown + 500){
// Ignore contextmenu/click events that were already processed in keydown handler below.
// But still call preventDefault() (above) so system context menu doesn't appear.
return;
}
// Schedule context menu to be opened.
// Note that this won't work will if the click was generated by the keyboard, while
// focused on a <button> etc. In that case evt.pageX and evt.pageY are either (0,0) or
// wherever the mouse cursor is. See keydown handler below.
self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY}, evt.target);
}),
on(cn, delegatedEvent("keydown"), function(evt){
if(evt.keyCode == 93 || // context menu key
(evt.shiftKey && evt.keyCode == keys.F10) || // shift-F10
(this.leftClickToOpen && evt.keyCode == keys.SPACE) // space key
){
evt.stopPropagation();
evt.preventDefault();
// Open the menu around evt.target. Note that "this" and evt.target
// are likely different, especially for global context menu, where "this" is <body>.
self._scheduleOpen(this, iframe, null, evt.target); // no coords - open near evt.target
this._lastKeyDown = (new Date()).getTime();
}
})
];
});
binding.connects = cn ? doConnects(cn) : [];
if(iframe){
// Setup handler to [re]bind to the iframe when the contents are initially loaded,
// and every time the contents change.
// Need to do this b/c we are actually binding to the iframe's <body> node.
// Note: can't use connect.connect(), see #9609.
binding.onloadHandler = lang.hitch(this, function(){
// want to remove old connections, but IE throws exceptions when trying to
// access the <body> node because it's already gone, or at least in a state of limbo
var window = this._iframeContentWindow(iframe),
cn = win.body(window.document);
binding.connects = doConnects(cn);
});
if(iframe.addEventListener){
iframe.addEventListener("load", binding.onloadHandler, false);
}else{
iframe.attachEvent("onload", binding.onloadHandler);
}
}
},
unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
// Detach menu from given node
var node;
try{
node = dom.byId(nodeName, this.ownerDocument);
}catch(e){
// On IE the dom.byId() call will get an exception if the attach point was
// the <body> node of an <iframe> that has since been reloaded (and thus the
// <body> node is in a limbo state of destruction.
return;
}
// node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
var attrName = "_dijitMenu" + this.id;
if(node && domAttr.has(node, attrName)){
var bid = domAttr.get(node, attrName) - 1, b = this._bindings[bid], h;
while((h = b.connects.pop())){
h.remove();
}
// Remove listener for iframe onload events
var iframe = b.iframe;
if(iframe){
if(iframe.removeEventListener){
iframe.removeEventListener("load", b.onloadHandler, false);
}else{
iframe.detachEvent("onload", b.onloadHandler);
}
}
domAttr.remove(node, attrName);
delete this._bindings[bid];
}
},
_scheduleOpen: function(delegatedTarget, iframe, coords, target){
// summary:
// Set timer to display myself. Using a timer rather than displaying immediately solves
// IE problem: without the delay, focus work in "open" causes the system
// context menu to appear in spite of evt.preventDefault().
// delegatedTarget: Element
// The node specified in targetNodeIds or matching selector that the menu is being opened for.
// iframe: HTMLIframeElement?
// Set if target is inside the specified iframe.
// coords: Object
// x/y position to center the menu around. Undefined if menu was opened via keyboard.
// target: Element
// The actual clicked node, either delegatedTarget or a descendant.
if(!this._openTimer){
this._openTimer = this.defer(function(){
delete this._openTimer;
this._openMyself({
target: target,
delegatedTarget: delegatedTarget,
iframe: iframe,
coords: coords
});
}, 1);
}
},
_openMyself: function(args){
// summary:
// Internal function for opening myself when the user does a right-click or something similar.
// args:
// This is an Object containing:
//
// - target: The node that is being clicked.
// - delegatedTarget: The node from this.targetNodeIds or matching this.selector,
// either the same as target or an ancestor of target.
// - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
// - coords: Mouse cursor x/y coordinates. Null when opened via keyboard.
// Put menu at specified position in iframe (if iframe specified) or otherwise in viewport.
//
// _openMyself() formerly took the event object, and since various code references
// evt.target (after connecting to _openMyself()), using an Object for parameters
// (so that old code still works).
var target = args.target,
iframe = args.iframe,
coords = args.coords,
byKeyboard = !coords;
// To be used by MenuItem event handlers to tell which node the menu was opened on
this.currentTarget = args.delegatedTarget;
// Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
// then near the node the menu is assigned to.
if(coords){
if(iframe){
// Specified coordinates are on <body> node of an <iframe>, convert to match main document
var ifc = domGeometry.position(iframe, true),
window = this._iframeContentWindow(iframe),
scroll = domGeometry.docScroll(window.document);
var cs = domStyle.getComputedStyle(iframe),
tp = domStyle.toPixelValue,
left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
coords.x += ifc.x + left - scroll.x;
coords.y += ifc.y + top - scroll.y;
}
}else{
coords = domGeometry.position(target, true);
coords.x += 10;
coords.y += 10;
}
var self = this;
var prevFocusNode = this._focusManager.get("prevNode");
var curFocusNode = this._focusManager.get("curNode");
var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
function closeAndRestoreFocus(){
// user has clicked on a menu or popup
if(self.refocus && savedFocusNode){
savedFocusNode.focus();
}
pm.close(self);
}
pm.open({
popup: this,
x: coords.x,
y: coords.y,
onExecute: closeAndRestoreFocus,
onCancel: closeAndRestoreFocus,
orient: this.isLeftToRight() ? 'L' : 'R'
});
// Focus the menu even when opened by mouse, so that a click on blank area of screen will close it
this.focus();
if(!byKeyboard){
// But then (when opened by mouse), mark Menu as passive, so that the first item isn't highlighted.
// On IE9+ this needs to be on a delay because the focus is asynchronous.
this.defer(function(){
this._cleanUp(true);
});
}
this._onBlur = function(){
this.inherited('_onBlur', arguments);
// Usually the parent closes the child widget but if this is a context
// menu then there is no parent
pm.close(this);
// don't try to restore focus; user has clicked another part of the screen
// and set focus there
};
},
destroy: function(){
array.forEach(this._bindings, function(b){
if(b){
this.unBindDomNode(b.node);
}
}, this);
this.inherited(arguments);
}
});
});
@@ -1,34 +1,89 @@
//>>built
require({cache:{"url:dijit/templates/MenuBar.html":"<div class=\"dijitMenuBar dijitMenuPassive\" data-dojo-attach-point=\"containerNode\" role=\"menubar\" tabIndex=\"${tabIndex}\"\n\t ></div>\n"}});
define("dijit/MenuBar",["dojo/_base/declare","dojo/keys","./_MenuBase","dojo/text!./templates/MenuBar.html"],function(_1,_2,_3,_4){
return _1("dijit.MenuBar",_3,{templateString:_4,baseClass:"dijitMenuBar",popupDelay:0,_isMenuBar:true,_orient:["below"],_moveToPopup:function(_5){
if(this.focusedChild&&this.focusedChild.popup&&!this.focusedChild.disabled){
this.onItemClick(this.focusedChild,_5);
}
},focusChild:function(_6){
this.inherited(arguments);
if(this.activated&&_6.popup&&!_6.disabled){
this._openItemPopup(_6,true);
}
},_onChildDeselect:function(_7){
if(this.currentPopupItem==_7){
this.currentPopupItem=null;
_7._closePopup();
}
this.inherited(arguments);
},_onLeftArrow:function(){
this.focusPrev();
},_onRightArrow:function(){
this.focusNext();
},_onDownArrow:function(_8){
this._moveToPopup(_8);
},_onUpArrow:function(){
},onItemClick:function(_9,_a){
if(_9.popup&&_9.popup.isShowingNow&&(!/^key/.test(_a.type)||_a.keyCode!==_2.DOWN_ARROW)){
_9.focusNode.focus();
this._cleanUp(true);
}else{
this.inherited(arguments);
}
}});
define([
"dojo/_base/declare", // declare
"dojo/keys", // keys.DOWN_ARROW
"./_MenuBase",
"dojo/text!./templates/MenuBar.html"
], function(declare, keys, _MenuBase, template){
// module:
// dijit/MenuBar
return declare("dijit.MenuBar", _MenuBase, {
// summary:
// A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
templateString: template,
baseClass: "dijitMenuBar",
// By default open popups for MenuBar instantly
popupDelay: 0,
// _isMenuBar: [protected] Boolean
// This is a MenuBar widget, not a (vertical) Menu widget.
_isMenuBar: true,
// parameter to dijit.popup.open() about where to put popup (relative to this.domNode)
_orient: ["below"],
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the down arrow key, opening a submenu if one exists.
// Unlike _MenuBase._moveToPopup(), will never move to the next item in the MenuBar.
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.onItemClick(this.focusedChild, evt);
}
},
focusChild: function(item){
// Overload focusChild so that whenever a new item is focused and the menu is active, open its submenu immediately.
this.inherited(arguments);
if(this.activated && item.popup && !item.disabled){
this._openItemPopup(item, true);
}
},
_onChildDeselect: function(item){
// override _MenuBase._onChildDeselect() to close submenu immediately
if(this.currentPopupItem == item){
this.currentPopupItem = null;
item._closePopup(); // this calls onClose
}
this.inherited(arguments);
},
// Arrow key navigation
_onLeftArrow: function(){
this.focusPrev();
},
_onRightArrow: function(){
this.focusNext();
},
_onDownArrow: function(/*Event*/ evt){
this._moveToPopup(evt);
},
_onUpArrow: function(){
},
onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item. Also called by _moveToPopup() due to a down-arrow key on the item.
// Cancels a dropdown if already open and click is either mouse or space/enter.
// Don't close dropdown due to down arrow.
// tags:
// private
if(item.popup && item.popup.isShowingNow && (!/^key/.test(evt.type) || evt.keyCode !== keys.DOWN_ARROW)){
item.focusNode.focus();
this._cleanUp(true);
}else{
this.inherited(arguments);
}
}
});
});
@@ -1,91 +0,0 @@
require({cache:{
'url:dijit/templates/MenuBar.html':"<div class=\"dijitMenuBar dijitMenuPassive\" data-dojo-attach-point=\"containerNode\" role=\"menubar\" tabIndex=\"${tabIndex}\"\n\t ></div>\n"}});
define("dijit/MenuBar", [
"dojo/_base/declare", // declare
"dojo/keys", // keys.DOWN_ARROW
"./_MenuBase",
"dojo/text!./templates/MenuBar.html"
], function(declare, keys, _MenuBase, template){
// module:
// dijit/MenuBar
return declare("dijit.MenuBar", _MenuBase, {
// summary:
// A menu bar, listing menu choices horizontally, like the "File" menu in most desktop applications
templateString: template,
baseClass: "dijitMenuBar",
// By default open popups for MenuBar instantly
popupDelay: 0,
// _isMenuBar: [protected] Boolean
// This is a MenuBar widget, not a (vertical) Menu widget.
_isMenuBar: true,
// parameter to dijit.popup.open() about where to put popup (relative to this.domNode)
_orient: ["below"],
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the down arrow key, opening a submenu if one exists.
// Unlike _MenuBase._moveToPopup(), will never move to the next item in the MenuBar.
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.onItemClick(this.focusedChild, evt);
}
},
focusChild: function(item){
// Overload focusChild so that whenever a new item is focused and the menu is active, open its submenu immediately.
this.inherited(arguments);
if(this.activated && item.popup && !item.disabled){
this._openItemPopup(item, true);
}
},
_onChildDeselect: function(item){
// override _MenuBase._onChildDeselect() to close submenu immediately
if(this.currentPopupItem == item){
this.currentPopupItem = null;
item._closePopup(); // this calls onClose
}
this.inherited(arguments);
},
// Arrow key navigation
_onLeftArrow: function(){
this.focusPrev();
},
_onRightArrow: function(){
this.focusNext();
},
_onDownArrow: function(/*Event*/ evt){
this._moveToPopup(evt);
},
_onUpArrow: function(){
},
onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item. Also called by _moveToPopup() due to a down-arrow key on the item.
// Cancels a dropdown if already open and click is either mouse or space/enter.
// Don't close dropdown due to down arrow.
// tags:
// private
if(item.popup && item.popup.isShowingNow && (!/^key/.test(evt.type) || evt.keyCode !== keys.DOWN_ARROW)){
item.focusNode.focus();
this._cleanUp(true);
}else{
this.inherited(arguments);
}
}
});
});
@@ -1,8 +1,26 @@
//>>built
require({cache:{"url:dijit/templates/MenuBarItem.html":"<div class=\"dijitReset dijitInline dijitMenuItem dijitMenuItemLabel\" data-dojo-attach-point=\"focusNode\"\n\t \trole=\"menuitem\" tabIndex=\"-1\">\n\t<span data-dojo-attach-point=\"containerNode,textDirNode\"></span>\n</div>\n"}});
define("dijit/MenuBarItem",["dojo/_base/declare","./MenuItem","dojo/text!./templates/MenuBarItem.html"],function(_1,_2,_3){
var _4=_1("dijit._MenuBarItemMixin",null,{templateString:_3,_setIconClassAttr:null});
var _5=_1("dijit.MenuBarItem",[_2,_4],{});
_5._MenuBarItemMixin=_4;
return _5;
define([
"dojo/_base/declare", // declare
"./MenuItem",
"dojo/text!./templates/MenuBarItem.html"
], function(declare, MenuItem, template){
// module:
// dijit/MenuBarItem
var _MenuBarItemMixin = declare("dijit._MenuBarItemMixin", null, {
templateString: template,
// Map widget attributes to DOMNode attributes.
_setIconClassAttr: null // cancel MenuItem setter because we don't have a place for an icon
});
var MenuBarItem = declare("dijit.MenuBarItem", [MenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
});
MenuBarItem._MenuBarItemMixin = _MenuBarItemMixin; // dojox.mobile is accessing this
return MenuBarItem;
});
@@ -1,28 +0,0 @@
require({cache:{
'url:dijit/templates/MenuBarItem.html':"<div class=\"dijitReset dijitInline dijitMenuItem dijitMenuItemLabel\" data-dojo-attach-point=\"focusNode\"\n\t \trole=\"menuitem\" tabIndex=\"-1\">\n\t<span data-dojo-attach-point=\"containerNode,textDirNode\"></span>\n</div>\n"}});
define("dijit/MenuBarItem", [
"dojo/_base/declare", // declare
"./MenuItem",
"dojo/text!./templates/MenuBarItem.html"
], function(declare, MenuItem, template){
// module:
// dijit/MenuBarItem
var _MenuBarItemMixin = declare("dijit._MenuBarItemMixin", null, {
templateString: template,
// Map widget attributes to DOMNode attributes.
_setIconClassAttr: null // cancel MenuItem setter because we don't have a place for an icon
});
var MenuBarItem = declare("dijit.MenuBarItem", [MenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar that's clickable, and doesn't spawn a submenu when pressed (or hovered)
});
MenuBarItem._MenuBarItemMixin = _MenuBarItemMixin; // dojox.mobile is accessing this
return MenuBarItem;
});
@@ -1,71 +1,182 @@
//>>built
require({cache:{"url:dijit/templates/MenuItem.html":"<tr class=\"dijitReset\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<span role=\"presentation\" class=\"dijitInline dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"></span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,textDirNode\"\n\t\trole=\"presentation\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<span data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<span class=\"dijitInline dijitIcon dijitMenuExpand\"></span>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</span>\n\t</td>\n</tr>\n"}});
define("dijit/MenuItem",["dojo/_base/declare","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/_base/kernel","dojo/sniff","dojo/_base/lang","./_Widget","./_TemplatedMixin","./_Contained","./_CssStateMixin","dojo/text!./templates/MenuItem.html"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c){
var _d=_1("dijit.MenuItem"+(_6("dojo-bidi")?"_NoBidi":""),[_8,_9,_a,_b],{templateString:_c,baseClass:"dijitMenuItem",label:"",_setLabelAttr:function(_e){
this._set("label",_e);
var _f="";
var _10;
var ndx=_e.search(/{\S}/);
if(ndx>=0){
_f=_e.charAt(ndx+1);
var _11=_e.substr(0,ndx);
var _12=_e.substr(ndx+3);
_10=_11+_f+_12;
_e=_11+"<span class=\"dijitMenuItemShortcutKey\">"+_f+"</span>"+_12;
}else{
_10=_e;
}
this.domNode.setAttribute("aria-label",_10+" "+this.accelKey);
this.containerNode.innerHTML=_e;
this._set("shortcutKey",_f);
},iconClass:"dijitNoIcon",_setIconClassAttr:{node:"iconNode",type:"class"},accelKey:"",disabled:false,_fillContent:function(_13){
if(_13&&!("label" in this.params)){
this._set("label",_13.innerHTML);
}
},buildRendering:function(){
this.inherited(arguments);
var _14=this.id+"_text";
_3.set(this.containerNode,"id",_14);
if(this.accelKeyNode){
_3.set(this.accelKeyNode,"id",this.id+"_accel");
}
_2.setSelectable(this.domNode,false);
},onClick:function(){
},focus:function(){
try{
if(_6("ie")==8){
this.containerNode.focus();
}
this.focusNode.focus();
}
catch(e){
}
},_setSelected:function(_15){
_4.toggle(this.domNode,"dijitMenuItemSelected",_15);
},setLabel:function(_16){
_5.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.","","2.0");
this.set("label",_16);
},setDisabled:function(_17){
_5.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.","","2.0");
this.set("disabled",_17);
},_setDisabledAttr:function(_18){
this.focusNode.setAttribute("aria-disabled",_18?"true":"false");
this._set("disabled",_18);
},_setAccelKeyAttr:function(_19){
if(this.accelKeyNode){
this.accelKeyNode.style.display=_19?"":"none";
this.accelKeyNode.innerHTML=_19;
_3.set(this.containerNode,"colSpan",_19?"1":"2");
}
this._set("accelKey",_19);
}});
if(_6("dojo-bidi")){
_d=_1("dijit.MenuItem",_d,{_setLabelAttr:function(val){
this.inherited(arguments);
if(this.textDir==="auto"){
this.applyTextDir(this.textDirNode);
}
}});
}
return _d;
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // kernel.deprecated
"dojo/sniff", // has("ie")
"dojo/_base/lang", // lang.hitch
"./_Widget",
"./_TemplatedMixin",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/MenuItem.html"
], function(declare, dom, domAttr, domClass, kernel, has, lang,
_Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
// module:
// dijit/MenuItem
var MenuItem = declare("dijit.MenuItem" + (has("dojo-bidi") ? "_NoBidi" : ""),
[_Widget, _TemplatedMixin, _Contained, _CssStateMixin], {
// summary:
// A line item in a Menu Widget
// Make 3 columns
// icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
templateString: template,
baseClass: "dijitMenuItem",
// label: String
// Menu text as HTML
label: "",
_setLabelAttr: function(val){
this._set("label", val);
var shortcutKey = "";
var text;
var ndx = val.search(/{\S}/);
if(ndx >= 0){
shortcutKey = val.charAt(ndx + 1);
var prefix = val.substr(0, ndx);
var suffix = val.substr(ndx + 3);
text = prefix + shortcutKey + suffix;
val = prefix + '<span class="dijitMenuItemShortcutKey">' + shortcutKey + '</span>' + suffix;
}else{
text = val;
}
this.domNode.setAttribute("aria-label", text + " " + this.accelKey);
this.containerNode.innerHTML = val;
this._set('shortcutKey', shortcutKey);
},
/*=====
// shortcutKey: [readonly] String
// Single character (underlined when the parent Menu is focused) used to navigate directly to this widget,
// also known as [a mnemonic](http://en.wikipedia.org/wiki/Mnemonics_(keyboard%29).
// This is denoted in the label by surrounding the single character with {}.
// For example, if label="{F}ile", then shortcutKey="F".
shortcutKey: "",
=====*/
// iconClass: String
// Class to apply to DOMNode to make it display an icon.
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
// accelKey: String
// Text for the accelerator (shortcut) key combination, a control, alt, etc. modified keystroke meant to
// execute the menu item regardless of where the focus is on the page.
//
// Note that although Menu can display accelerator keys, there is no infrastructure to actually catch and
// execute those accelerators.
accelKey: "",
// disabled: Boolean
// If true, the menu item is disabled.
// If false, the menu item is enabled.
disabled: false,
_fillContent: function(/*DomNode*/ source){
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
if(source && !("label" in this.params)){
this._set('label', source.innerHTML);
}
},
buildRendering: function(){
this.inherited(arguments);
var label = this.id + "_text";
domAttr.set(this.containerNode, "id", label); // only needed for backward compat
if(this.accelKeyNode){
domAttr.set(this.accelKeyNode, "id", this.id + "_accel"); // only needed for backward compat
}
dom.setSelectable(this.domNode, false);
},
onClick: function(/*Event*/){
// summary:
// User defined function to handle clicks
// tags:
// callback
},
focus: function(){
// summary:
// Focus on this MenuItem
try{
if(has("ie") == 8){
// needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
this.containerNode.focus();
}
this.focusNode.focus();
}catch(e){
// this throws on IE (at least) in some scenarios
}
},
_setSelected: function(selected){
// summary:
// Indicate that this node is the currently selected one
// tags:
// private
domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', bool) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ value){
// summary:
// Hook for attr('disabled', ...) to work.
// Enable or disable this menu item.
this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
this._set("disabled", value);
},
_setAccelKeyAttr: function(/*String*/ value){
// summary:
// Hook for attr('accelKey', ...) to work.
// Set accelKey on this menu item.
if(this.accelKeyNode){
this.accelKeyNode.style.display = value ? "" : "none";
this.accelKeyNode.innerHTML = value;
//have to use colSpan to make it work in IE
domAttr.set(this.containerNode, 'colSpan', value ? "1" : "2");
}
this._set("accelKey", value);
}
});
if(has("dojo-bidi")){
MenuItem = declare("dijit.MenuItem", MenuItem, {
_setLabelAttr: function(val){
this.inherited(arguments);
if(this.textDir === "auto"){
this.applyTextDir(this.textDirNode);
}
}
});
}
return MenuItem;
});
@@ -1,184 +0,0 @@
require({cache:{
'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<span role=\"presentation\" class=\"dijitInline dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"></span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,textDirNode\"\n\t\trole=\"presentation\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<span data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<span class=\"dijitInline dijitIcon dijitMenuExpand\"></span>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</span>\n\t</td>\n</tr>\n"}});
define("dijit/MenuItem", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // kernel.deprecated
"dojo/sniff", // has("ie")
"dojo/_base/lang", // lang.hitch
"./_Widget",
"./_TemplatedMixin",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/MenuItem.html"
], function(declare, dom, domAttr, domClass, kernel, has, lang,
_Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
// module:
// dijit/MenuItem
var MenuItem = declare("dijit.MenuItem" + (has("dojo-bidi") ? "_NoBidi" : ""),
[_Widget, _TemplatedMixin, _Contained, _CssStateMixin], {
// summary:
// A line item in a Menu Widget
// Make 3 columns
// icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
templateString: template,
baseClass: "dijitMenuItem",
// label: String
// Menu text as HTML
label: "",
_setLabelAttr: function(val){
this._set("label", val);
var shortcutKey = "";
var text;
var ndx = val.search(/{\S}/);
if(ndx >= 0){
shortcutKey = val.charAt(ndx + 1);
var prefix = val.substr(0, ndx);
var suffix = val.substr(ndx + 3);
text = prefix + shortcutKey + suffix;
val = prefix + '<span class="dijitMenuItemShortcutKey">' + shortcutKey + '</span>' + suffix;
}else{
text = val;
}
this.domNode.setAttribute("aria-label", text + " " + this.accelKey);
this.containerNode.innerHTML = val;
this._set('shortcutKey', shortcutKey);
},
/*=====
// shortcutKey: [readonly] String
// Single character (underlined when the parent Menu is focused) used to navigate directly to this widget,
// also known as [a mnemonic](http://en.wikipedia.org/wiki/Mnemonics_(keyboard%29).
// This is denoted in the label by surrounding the single character with {}.
// For example, if label="{F}ile", then shortcutKey="F".
shortcutKey: "",
=====*/
// iconClass: String
// Class to apply to DOMNode to make it display an icon.
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
// accelKey: String
// Text for the accelerator (shortcut) key combination, a control, alt, etc. modified keystroke meant to
// execute the menu item regardless of where the focus is on the page.
//
// Note that although Menu can display accelerator keys, there is no infrastructure to actually catch and
// execute those accelerators.
accelKey: "",
// disabled: Boolean
// If true, the menu item is disabled.
// If false, the menu item is enabled.
disabled: false,
_fillContent: function(/*DomNode*/ source){
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
if(source && !("label" in this.params)){
this._set('label', source.innerHTML);
}
},
buildRendering: function(){
this.inherited(arguments);
var label = this.id + "_text";
domAttr.set(this.containerNode, "id", label); // only needed for backward compat
if(this.accelKeyNode){
domAttr.set(this.accelKeyNode, "id", this.id + "_accel"); // only needed for backward compat
}
dom.setSelectable(this.domNode, false);
},
onClick: function(/*Event*/){
// summary:
// User defined function to handle clicks
// tags:
// callback
},
focus: function(){
// summary:
// Focus on this MenuItem
try{
if(has("ie") == 8){
// needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
this.containerNode.focus();
}
this.focusNode.focus();
}catch(e){
// this throws on IE (at least) in some scenarios
}
},
_setSelected: function(selected){
// summary:
// Indicate that this node is the currently selected one
// tags:
// private
domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', bool) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ value){
// summary:
// Hook for attr('disabled', ...) to work.
// Enable or disable this menu item.
this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
this._set("disabled", value);
},
_setAccelKeyAttr: function(/*String*/ value){
// summary:
// Hook for attr('accelKey', ...) to work.
// Set accelKey on this menu item.
if(this.accelKeyNode){
this.accelKeyNode.style.display = value ? "" : "none";
this.accelKeyNode.innerHTML = value;
//have to use colSpan to make it work in IE
domAttr.set(this.containerNode, 'colSpan', value ? "1" : "2");
}
this._set("accelKey", value);
}
});
if(has("dojo-bidi")){
MenuItem = declare("dijit.MenuItem", MenuItem, {
_setLabelAttr: function(val){
this.inherited(arguments);
if(this.textDir === "auto"){
this.applyTextDir(this.textDirNode);
}
}
});
}
return MenuItem;
});
@@ -1,10 +1,33 @@
//>>built
require({cache:{"url:dijit/templates/MenuSeparator.html":"<tr class=\"dijitMenuSeparator\" role=\"separator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n"}});
define("dijit/MenuSeparator",["dojo/_base/declare","dojo/dom","./_WidgetBase","./_TemplatedMixin","./_Contained","dojo/text!./templates/MenuSeparator.html"],function(_1,_2,_3,_4,_5,_6){
return _1("dijit.MenuSeparator",[_3,_4,_5],{templateString:_6,buildRendering:function(){
this.inherited(arguments);
_2.setSelectable(this.domNode,false);
},isFocusable:function(){
return false;
}});
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_WidgetBase",
"./_TemplatedMixin",
"./_Contained",
"dojo/text!./templates/MenuSeparator.html"
], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
// module:
// dijit/MenuSeparator
return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
// summary:
// A line between two menu items
templateString: template,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// Override to always return false
// tags:
// protected
return false; // Boolean
}
});
});
@@ -1,35 +0,0 @@
require({cache:{
'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\" role=\"separator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n"}});
define("dijit/MenuSeparator", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_WidgetBase",
"./_TemplatedMixin",
"./_Contained",
"dojo/text!./templates/MenuSeparator.html"
], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
// module:
// dijit/MenuSeparator
return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
// summary:
// A line between two menu items
templateString: template,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// Override to always return false
// tags:
// protected
return false; // Boolean
}
});
});
@@ -1,5 +1,16 @@
//>>built
define("dijit/PopupMenuBarItem",["dojo/_base/declare","./PopupMenuItem","./MenuBarItem"],function(_1,_2,_3){
var _4=_3._MenuBarItemMixin;
return _1("dijit.PopupMenuBarItem",[_2,_4],{});
define([
"dojo/_base/declare", // declare
"./PopupMenuItem",
"./MenuBarItem"
], function(declare, PopupMenuItem, MenuBarItem){
// module:
// dijit/PopupMenuBarItem
var _MenuBarItemMixin = MenuBarItem._MenuBarItemMixin;
return declare("dijit.PopupMenuBarItem", [PopupMenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
});
});
@@ -1,16 +0,0 @@
define("dijit/PopupMenuBarItem", [
"dojo/_base/declare", // declare
"./PopupMenuItem",
"./MenuBarItem"
], function(declare, PopupMenuItem, MenuBarItem){
// module:
// dijit/PopupMenuBarItem
var _MenuBarItemMixin = MenuBarItem._MenuBarItemMixin;
return declare("dijit.PopupMenuBarItem", [PopupMenuItem, _MenuBarItemMixin], {
// summary:
// Item in a MenuBar like "File" or "Edit", that spawns a submenu when pressed (or hovered)
});
});
@@ -1,44 +1,101 @@
//>>built
define("dijit/PopupMenuItem",["dojo/_base/declare","dojo/dom-style","dojo/_base/lang","dojo/query","./popup","./registry","./MenuItem","./hccss"],function(_1,_2,_3,_4,pm,_5,_6){
return _1("dijit.PopupMenuItem",_6,{baseClass:"dijitMenuItem dijitPopupMenuItem",_fillContent:function(){
if(this.srcNodeRef){
var _7=_4("*",this.srcNodeRef);
this.inherited(arguments,[_7[0]]);
this.dropDownContainer=this.srcNodeRef;
}
},_openPopup:function(_8,_9){
var _a=this.popup;
pm.open(_3.delegate(_8,{popup:this.popup,around:this.domNode}));
if(_9&&_a.focus){
_a.focus();
}
},_closePopup:function(){
pm.close(this.popup);
this.popup.parentMenu=null;
},startup:function(){
if(this._started){
return;
}
this.inherited(arguments);
if(!this.popup){
var _b=_4("[widgetId]",this.dropDownContainer)[0];
this.popup=_5.byNode(_b);
}
this.ownerDocumentBody.appendChild(this.popup.domNode);
this.popup.domNode.setAttribute("aria-labelledby",this.containerNode.id);
this.popup.startup();
this.popup.domNode.style.display="none";
if(this.arrowWrapper){
_2.set(this.arrowWrapper,"visibility","");
}
this.focusNode.setAttribute("aria-haspopup","true");
},destroyDescendants:function(_c){
if(this.popup){
if(!this.popup._destroyed){
this.popup.destroyRecursive(_c);
}
delete this.popup;
}
this.inherited(arguments);
}});
define([
"dojo/_base/declare", // declare
"dojo/dom-style", // domStyle.set
"dojo/_base/lang",
"dojo/query", // query
"./popup",
"./registry", // registry.byNode
"./MenuItem",
"./hccss"
], function(declare, domStyle, lang, query, pm, registry, MenuItem){
// module:
// dijit/PopupMenuItem
return declare("dijit.PopupMenuItem", MenuItem, {
// summary:
// An item in a Menu that spawn a drop down (usually a drop down menu)
baseClass: "dijitMenuItem dijitPopupMenuItem",
_fillContent: function(){
// summary:
// When Menu is declared in markup, this code gets the menu label and
// the popup widget from the srcNodeRef.
// description:
// srcNodeRef.innerHTML contains both the menu item text and a popup widget
// The first part holds the menu item text and the second part is the popup
// example:
// | <div data-dojo-type="dijit/PopupMenuItem">
// | <span>pick me</span>
// | <popup> ... </popup>
// | </div>
// tags:
// protected
if(this.srcNodeRef){
var nodes = query("*", this.srcNodeRef);
this.inherited(arguments, [nodes[0]]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
_openPopup: function(/*Object*/ params, /*Boolean*/ focus){
// summary:
// Open the popup to the side of/underneath this MenuItem, and optionally focus first item
// tags:
// protected
var popup = this.popup;
pm.open(lang.delegate(params, {
popup: this.popup,
around: this.domNode
}));
if(focus && popup.focus){
popup.focus();
}
},
_closePopup: function(){
pm.close(this.popup);
this.popup.parentMenu = null;
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
// We didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
// land now. Move it to <body>.
if(!this.popup){
var node = query("[widgetId]", this.dropDownContainer)[0];
this.popup = registry.byNode(node);
}
this.ownerDocumentBody.appendChild(this.popup.domNode);
this.popup.domNode.setAttribute("aria-labelledby", this.containerNode.id);
this.popup.startup();
this.popup.domNode.style.display="none";
if(this.arrowWrapper){
domStyle.set(this.arrowWrapper, "visibility", "");
}
this.focusNode.setAttribute("aria-haspopup", "true");
},
destroyDescendants: function(/*Boolean*/ preserveDom){
if(this.popup){
// Destroy the popup, unless it's already been destroyed. This can happen because
// the popup is a direct child of <body> even though it's logically my child.
if(!this.popup._destroyed){
this.popup.destroyRecursive(preserveDom);
}
delete this.popup;
}
this.inherited(arguments);
}
});
});
@@ -1,101 +0,0 @@
define("dijit/PopupMenuItem", [
"dojo/_base/declare", // declare
"dojo/dom-style", // domStyle.set
"dojo/_base/lang",
"dojo/query", // query
"./popup",
"./registry", // registry.byNode
"./MenuItem",
"./hccss"
], function(declare, domStyle, lang, query, pm, registry, MenuItem){
// module:
// dijit/PopupMenuItem
return declare("dijit.PopupMenuItem", MenuItem, {
// summary:
// An item in a Menu that spawn a drop down (usually a drop down menu)
baseClass: "dijitMenuItem dijitPopupMenuItem",
_fillContent: function(){
// summary:
// When Menu is declared in markup, this code gets the menu label and
// the popup widget from the srcNodeRef.
// description:
// srcNodeRef.innerHTML contains both the menu item text and a popup widget
// The first part holds the menu item text and the second part is the popup
// example:
// | <div data-dojo-type="dijit/PopupMenuItem">
// | <span>pick me</span>
// | <popup> ... </popup>
// | </div>
// tags:
// protected
if(this.srcNodeRef){
var nodes = query("*", this.srcNodeRef);
this.inherited(arguments, [nodes[0]]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
_openPopup: function(/*Object*/ params, /*Boolean*/ focus){
// summary:
// Open the popup to the side of/underneath this MenuItem, and optionally focus first item
// tags:
// protected
var popup = this.popup;
pm.open(lang.delegate(params, {
popup: this.popup,
around: this.domNode
}));
if(focus && popup.focus){
popup.focus();
}
},
_closePopup: function(){
pm.close(this.popup);
this.popup.parentMenu = null;
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
// We didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
// land now. Move it to <body>.
if(!this.popup){
var node = query("[widgetId]", this.dropDownContainer)[0];
this.popup = registry.byNode(node);
}
this.ownerDocumentBody.appendChild(this.popup.domNode);
this.popup.domNode.setAttribute("aria-labelledby", this.containerNode.id);
this.popup.startup();
this.popup.domNode.style.display="none";
if(this.arrowWrapper){
domStyle.set(this.arrowWrapper, "visibility", "");
}
this.focusNode.setAttribute("aria-haspopup", "true");
},
destroyDescendants: function(/*Boolean*/ preserveDom){
if(this.popup){
// Destroy the popup, unless it's already been destroyed. This can happen because
// the popup is a direct child of <body> even though it's logically my child.
if(!this.popup._destroyed){
this.popup.destroyRecursive(preserveDom);
}
delete this.popup;
}
this.inherited(arguments);
}
});
});
@@ -1,59 +1,170 @@
//>>built
require({cache:{"url:dijit/templates/ProgressBar.html":"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><span data-dojo-attach-point=\"indeterminateHighContrastImage\"\n\t\t class=\"dijitInline dijitProgressBarIndeterminateHighContrastImage\"></span\n></div>\n"}});
define("dijit/ProgressBar",["require","dojo/_base/declare","dojo/dom-class","dojo/_base/lang","dojo/number","./_Widget","./_TemplatedMixin","dojo/text!./templates/ProgressBar.html"],function(_1,_2,_3,_4,_5,_6,_7,_8){
return _2("dijit.ProgressBar",[_6,_7],{progress:"0",value:"",maximum:100,places:0,indeterminate:false,label:"",name:"",templateString:_8,_indeterminateHighContrastImagePath:_1.toUrl("./themes/a11y/indeterminate_progress.gif"),postMixInProperties:function(){
this.inherited(arguments);
if(!(this.params&&"value" in this.params)){
this.value=this.indeterminate?Infinity:this.progress;
}
},buildRendering:function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",this._indeterminateHighContrastImagePath.toString());
this.update();
},_setDirAttr:function(_9){
var _a=_9.toLowerCase()=="rtl";
_3.toggle(this.domNode,"dijitProgressBarRtl",_a);
_3.toggle(this.domNode,"dijitProgressBarIndeterminateRtl",this.indeterminate&&_a);
this.inherited(arguments);
},update:function(_b){
_4.mixin(this,_b||{});
var _c=this.internalProgress,ap=this.domNode;
var _d=1;
if(this.indeterminate){
ap.removeAttribute("aria-valuenow");
}else{
if(String(this.progress).indexOf("%")!=-1){
_d=Math.min(parseFloat(this.progress)/100,1);
this.progress=_d*this.maximum;
}else{
this.progress=Math.min(this.progress,this.maximum);
_d=this.maximum?this.progress/this.maximum:0;
}
ap.setAttribute("aria-valuenow",this.progress);
}
ap.setAttribute("aria-labelledby",this.labelNode.id);
ap.setAttribute("aria-valuemin",0);
ap.setAttribute("aria-valuemax",this.maximum);
this.labelNode.innerHTML=this.report(_d);
_3.toggle(this.domNode,"dijitProgressBarIndeterminate",this.indeterminate);
_3.toggle(this.domNode,"dijitProgressBarIndeterminateRtl",this.indeterminate&&!this.isLeftToRight());
_c.style.width=(_d*100)+"%";
this.onChange();
},_setValueAttr:function(v){
this._set("value",v);
if(v==Infinity){
this.update({indeterminate:true});
}else{
this.update({indeterminate:false,progress:v});
}
},_setLabelAttr:function(_e){
this._set("label",_e);
this.update();
},_setIndeterminateAttr:function(_f){
this._set("indeterminate",_f);
this.update();
},report:function(_10){
return this.label?this.label:(this.indeterminate?"&#160;":_5.format(_10,{type:"percent",places:this.places,locale:this.lang}));
},onChange:function(){
}});
define([
"require", // require.toUrl
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/_base/lang", // lang.mixin
"dojo/number", // number.format
"./_Widget",
"./_TemplatedMixin",
"dojo/text!./templates/ProgressBar.html"
], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
// module:
// dijit/ProgressBar
return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
// progress: [const] String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// Deprecated. Use "value" instead.
progress: "0",
// value: String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// With "%": percentage value, 0% <= progress <= 100%, or
// without "%": absolute value, 0 <= progress <= maximum.
// Infinity means that the progress bar is indeterminate.
value: "",
// maximum: [const] Float
// Max sample number
maximum: 100,
// places: [const] Number
// Number of places to show in values; 0 by default
places: 0,
// indeterminate: [const] Boolean
// If false: show progress value (number or percentage).
// If true: show that a process is underway but that the amount completed is unknown.
// Deprecated. Use "value" instead.
indeterminate: false,
// label: String?
// HTML label on progress bar. Defaults to percentage for determinate progress bar and
// blank for indeterminate progress bar.
label: "",
// name: String
// this is the field name (for a form) if set. This needs to be set if you want to use
// this widget in a dijit/form/Form widget (such as dijit/Dialog)
name: '',
templateString: template,
// _indeterminateHighContrastImagePath: [private] URL
// URL to image to use for indeterminate progress bar when display is in high contrast mode
_indeterminateHighContrastImagePath: require.toUrl("./themes/a11y/indeterminate_progress.gif"),
postMixInProperties: function(){
this.inherited(arguments);
// Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
if(!(this.params && "value" in this.params)){
this.value = this.indeterminate ? Infinity : this.progress;
}
},
buildRendering: function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",
this._indeterminateHighContrastImagePath.toString());
this.update();
},
_setDirAttr: function(val){
// Normally _CssStateMixin takes care of this, but we aren't extending it
var rtl = val.toLowerCase() == "rtl";
domClass.toggle(this.domNode, "dijitProgressBarRtl", rtl);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminateRtl", this.indeterminate && rtl);
this.inherited(arguments);
},
update: function(/*Object?*/attributes){
// summary:
// Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
// set("value", ...) rather than calling this method directly.
// attributes:
// May provide progress and/or maximum properties on this parameter;
// see attribute specs for details.
// example:
// | myProgressBar.update({'indeterminate': true});
// | myProgressBar.update({'progress': 80});
// | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
// tags:
// private
// TODO: deprecate this method and use set() instead
lang.mixin(this, attributes || {});
var tip = this.internalProgress, ap = this.domNode;
var percent = 1;
if(this.indeterminate){
ap.removeAttribute("aria-valuenow");
}else{
if(String(this.progress).indexOf("%") != -1){
percent = Math.min(parseFloat(this.progress) / 100, 1);
this.progress = percent * this.maximum;
}else{
this.progress = Math.min(this.progress, this.maximum);
percent = this.maximum ? this.progress / this.maximum : 0;
}
ap.setAttribute("aria-valuenow", this.progress);
}
// Even indeterminate ProgressBars should have these attributes
ap.setAttribute("aria-labelledby", this.labelNode.id);
ap.setAttribute("aria-valuemin", 0);
ap.setAttribute("aria-valuemax", this.maximum);
this.labelNode.innerHTML = this.report(percent);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminateRtl", this.indeterminate && !this.isLeftToRight());
tip.style.width = (percent * 100) + "%";
this.onChange();
},
_setValueAttr: function(v){
this._set("value", v);
if(v == Infinity){
this.update({indeterminate: true});
}else{
this.update({indeterminate: false, progress: v});
}
},
_setLabelAttr: function(label){
this._set("label", label);
this.update();
},
_setIndeterminateAttr: function(indeterminate){
// Deprecated, use set("value", ...) instead
this._set("indeterminate", indeterminate);
this.update();
},
report: function(/*float*/percent){
// summary:
// Generates HTML message to show inside progress bar (normally indicating amount of task completed).
// May be overridden.
// tags:
// extension
return this.label ? this.label :
(this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
},
onChange: function(){
// summary:
// Callback fired when progress updates.
// tags:
// extension
}
});
});
@@ -1,172 +0,0 @@
require({cache:{
'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><span data-dojo-attach-point=\"indeterminateHighContrastImage\"\n\t\t class=\"dijitInline dijitProgressBarIndeterminateHighContrastImage\"></span\n></div>\n"}});
define("dijit/ProgressBar", [
"require", // require.toUrl
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/_base/lang", // lang.mixin
"dojo/number", // number.format
"./_Widget",
"./_TemplatedMixin",
"dojo/text!./templates/ProgressBar.html"
], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
// module:
// dijit/ProgressBar
return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
// progress: [const] String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// Deprecated. Use "value" instead.
progress: "0",
// value: String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// With "%": percentage value, 0% <= progress <= 100%, or
// without "%": absolute value, 0 <= progress <= maximum.
// Infinity means that the progress bar is indeterminate.
value: "",
// maximum: [const] Float
// Max sample number
maximum: 100,
// places: [const] Number
// Number of places to show in values; 0 by default
places: 0,
// indeterminate: [const] Boolean
// If false: show progress value (number or percentage).
// If true: show that a process is underway but that the amount completed is unknown.
// Deprecated. Use "value" instead.
indeterminate: false,
// label: String?
// HTML label on progress bar. Defaults to percentage for determinate progress bar and
// blank for indeterminate progress bar.
label: "",
// name: String
// this is the field name (for a form) if set. This needs to be set if you want to use
// this widget in a dijit/form/Form widget (such as dijit/Dialog)
name: '',
templateString: template,
// _indeterminateHighContrastImagePath: [private] URL
// URL to image to use for indeterminate progress bar when display is in high contrast mode
_indeterminateHighContrastImagePath: require.toUrl("./themes/a11y/indeterminate_progress.gif"),
postMixInProperties: function(){
this.inherited(arguments);
// Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
if(!(this.params && "value" in this.params)){
this.value = this.indeterminate ? Infinity : this.progress;
}
},
buildRendering: function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",
this._indeterminateHighContrastImagePath.toString());
this.update();
},
_setDirAttr: function(val){
// Normally _CssStateMixin takes care of this, but we aren't extending it
var rtl = val.toLowerCase() == "rtl";
domClass.toggle(this.domNode, "dijitProgressBarRtl", rtl);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminateRtl", this.indeterminate && rtl);
this.inherited(arguments);
},
update: function(/*Object?*/attributes){
// summary:
// Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
// set("value", ...) rather than calling this method directly.
// attributes:
// May provide progress and/or maximum properties on this parameter;
// see attribute specs for details.
// example:
// | myProgressBar.update({'indeterminate': true});
// | myProgressBar.update({'progress': 80});
// | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
// tags:
// private
// TODO: deprecate this method and use set() instead
lang.mixin(this, attributes || {});
var tip = this.internalProgress, ap = this.domNode;
var percent = 1;
if(this.indeterminate){
ap.removeAttribute("aria-valuenow");
}else{
if(String(this.progress).indexOf("%") != -1){
percent = Math.min(parseFloat(this.progress) / 100, 1);
this.progress = percent * this.maximum;
}else{
this.progress = Math.min(this.progress, this.maximum);
percent = this.maximum ? this.progress / this.maximum : 0;
}
ap.setAttribute("aria-valuenow", this.progress);
}
// Even indeterminate ProgressBars should have these attributes
ap.setAttribute("aria-labelledby", this.labelNode.id);
ap.setAttribute("aria-valuemin", 0);
ap.setAttribute("aria-valuemax", this.maximum);
this.labelNode.innerHTML = this.report(percent);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminateRtl", this.indeterminate && !this.isLeftToRight());
tip.style.width = (percent * 100) + "%";
this.onChange();
},
_setValueAttr: function(v){
this._set("value", v);
if(v == Infinity){
this.update({indeterminate: true});
}else{
this.update({indeterminate: false, progress: v});
}
},
_setLabelAttr: function(label){
this._set("label", label);
this.update();
},
_setIndeterminateAttr: function(indeterminate){
// Deprecated, use set("value", ...) instead
this._set("indeterminate", indeterminate);
this.update();
},
report: function(/*float*/percent){
// summary:
// Generates HTML message to show inside progress bar (normally indicating amount of task completed).
// May be overridden.
// tags:
// extension
return this.label ? this.label :
(this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
},
onChange: function(){
// summary:
// Callback fired when progress updates.
// tags:
// extension
}
});
});
@@ -1,31 +1,73 @@
//>>built
define("dijit/RadioMenuItem",["dojo/_base/array","dojo/_base/declare","dojo/dom-class","dojo/query!css2","./CheckedMenuItem","./registry"],function(_1,_2,_3,_4,_5,_6){
return _2("dijit.RadioButtonMenuItem",_5,{baseClass:"dijitMenuItem dijitRadioMenuItem",role:"menuitemradio",checkedChar:"*",group:"",_setGroupAttr:"domNode",_setCheckedAttr:function(_7){
this.inherited(arguments);
if(!this._created){
return;
}
if(_7&&this.group){
_1.forEach(this._getRelatedWidgets(),function(_8){
if(_8!=this&&_8.checked){
_8.set("checked",false);
}
},this);
}
},_onClick:function(_9){
if(!this.disabled&&!this.checked){
this.set("checked",true);
this.onChange(true);
}
this.onClick(_9);
},_getRelatedWidgets:function(){
var _a=[];
_4("[group="+this.group+"][role="+this.role+"]").forEach(function(_b){
var _c=_6.getEnclosingWidget(_b);
if(_c){
_a.push(_c);
}
});
return _a;
}});
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/query!css2", // query
"./CheckedMenuItem",
"./registry" // registry.getEnclosingWidget
], function(array, declare, domClass, query, CheckedMenuItem, registry){
// module:
// dijit/RadioButtonMenuItem
return declare("dijit.RadioButtonMenuItem", CheckedMenuItem, {
// summary:
// A radio-button-like menu item for toggling on and off
// Use both base classes so we get styles like dijitMenuItemDisabled
baseClass: "dijitMenuItem dijitRadioMenuItem",
role: "menuitemradio",
// checkedChar: String
// Character (or string) used in place of radio button icon when display in high contrast mode
checkedChar: "*",
// group: String
// Toggling on a RadioMenuItem in a given group toggles off the other RadioMenuItems in that group.
group: "",
_setGroupAttr: "domNode", // needs to be set as an attribute so dojo/query can find it
_setCheckedAttr: function(/*Boolean*/ checked){
// If I am being checked then have to deselect currently checked items
this.inherited(arguments);
if(!this._created){
return;
}
if(checked && this.group){
array.forEach(this._getRelatedWidgets(), function(widget){
if(widget != this && widget.checked){
widget.set('checked', false);
}
}, this);
}
},
_onClick: function(evt){
// summary:
// Clicking this item toggles it on. If it's already on, then clicking does nothing.
// tags:
// private
if(!this.disabled && !this.checked){
this.set("checked", true);
this.onChange(true);
}
this.onClick(evt);
},
_getRelatedWidgets: function(){
// Private function needed to help iterate over all radio menu items in a group.
var ary = [];
query("[group=" + this.group + "][role=" + this.role + "]").forEach(
function(menuItemNode){
var widget = registry.getEnclosingWidget(menuItemNode);
if(widget){
ary.push(widget);
}
}
);
return ary;
}
});
});
@@ -1,73 +0,0 @@
define("dijit/RadioMenuItem", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/query!css2", // query
"./CheckedMenuItem",
"./registry" // registry.getEnclosingWidget
], function(array, declare, domClass, query, CheckedMenuItem, registry){
// module:
// dijit/RadioButtonMenuItem
return declare("dijit.RadioButtonMenuItem", CheckedMenuItem, {
// summary:
// A radio-button-like menu item for toggling on and off
// Use both base classes so we get styles like dijitMenuItemDisabled
baseClass: "dijitMenuItem dijitRadioMenuItem",
role: "menuitemradio",
// checkedChar: String
// Character (or string) used in place of radio button icon when display in high contrast mode
checkedChar: "*",
// group: String
// Toggling on a RadioMenuItem in a given group toggles off the other RadioMenuItems in that group.
group: "",
_setGroupAttr: "domNode", // needs to be set as an attribute so dojo/query can find it
_setCheckedAttr: function(/*Boolean*/ checked){
// If I am being checked then have to deselect currently checked items
this.inherited(arguments);
if(!this._created){
return;
}
if(checked && this.group){
array.forEach(this._getRelatedWidgets(), function(widget){
if(widget != this && widget.checked){
widget.set('checked', false);
}
}, this);
}
},
_onClick: function(evt){
// summary:
// Clicking this item toggles it on. If it's already on, then clicking does nothing.
// tags:
// private
if(!this.disabled && !this.checked){
this.set("checked", true);
this.onChange(true);
}
this.onClick(evt);
},
_getRelatedWidgets: function(){
// Private function needed to help iterate over all radio menu items in a group.
var ary = [];
query("[group=" + this.group + "][role=" + this.role + "]").forEach(
function(menuItemNode){
var widget = registry.getEnclosingWidget(menuItemNode);
if(widget){
ary.push(widget);
}
}
);
return ary;
}
});
});
@@ -1,112 +1,292 @@
//>>built
require({cache:{"url:dijit/templates/TitlePane.html":"<div>\n\t<div data-dojo-attach-event=\"ondijitclick:_onTitleClick, onkeydown:_onTitleKey\"\n\t\t\tclass=\"dijitTitlePaneTitle\" data-dojo-attach-point=\"titleBarNode\" id=\"${id}_titleBarNode\">\n\t\t<div class=\"dijitTitlePaneTitleFocus\" data-dojo-attach-point=\"focusNode\">\n\t\t\t<span data-dojo-attach-point=\"arrowNode\" class=\"dijitInline dijitArrowNode\" role=\"presentation\"></span\n\t\t\t><span data-dojo-attach-point=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t\t><span data-dojo-attach-point=\"titleNode\" class=\"dijitTitlePaneTextNode\"></span>\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" data-dojo-attach-point=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" data-dojo-attach-point=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" data-dojo-attach-point=\"containerNode\" role=\"region\" id=\"${id}_pane\" aria-labelledby=\"${id}_titleBarNode\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"}});
define("dijit/TitlePane",["dojo/_base/array","dojo/_base/declare","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/dom-geometry","dojo/fx","dojo/has","dojo/_base/kernel","dojo/keys","./_CssStateMixin","./_TemplatedMixin","./layout/ContentPane","dojo/text!./templates/TitlePane.html","./_base/manager","./a11yclick"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f){
var _10=_2("dijit.TitlePane",[_d,_c,_b],{title:"",_setTitleAttr:{node:"titleNode",type:"innerHTML"},open:true,toggleable:true,tabIndex:"0",duration:_f.defaultDuration,baseClass:"dijitTitlePane",templateString:_e,doLayout:false,_setTooltipAttr:{node:"focusNode",type:"attribute",attribute:"title"},buildRendering:function(){
this.inherited(arguments);
_3.setSelectable(this.titleNode,false);
},postCreate:function(){
this.inherited(arguments);
if(this.toggleable){
this._trackMouseState(this.titleBarNode,this.baseClass+"Title");
}
var _11=this.hideNode,_12=this.wipeNode;
this._wipeIn=_7.wipeIn({node:_12,duration:this.duration,beforeBegin:function(){
_11.style.display="";
}});
this._wipeOut=_7.wipeOut({node:_12,duration:this.duration,onEnd:function(){
_11.style.display="none";
}});
},_setOpenAttr:function(_13,_14){
_1.forEach([this._wipeIn,this._wipeOut],function(_15){
if(_15&&_15.status()=="playing"){
_15.stop();
}
});
if(_14){
var _16=this[_13?"_wipeIn":"_wipeOut"];
_16.play();
}else{
this.hideNode.style.display=this.wipeNode.style.display=_13?"":"none";
}
if(this._started){
if(_13){
this._onShow();
}else{
this.onHide();
}
}
this.containerNode.setAttribute("aria-hidden",_13?"false":"true");
this.focusNode.setAttribute("aria-pressed",_13?"true":"false");
this._set("open",_13);
this._setCss();
},_setToggleableAttr:function(_17){
this.focusNode.setAttribute("role",_17?"button":"heading");
if(_17){
this.focusNode.setAttribute("aria-controls",this.id+"_pane");
this.focusNode.setAttribute("tabIndex",this.tabIndex);
this.focusNode.setAttribute("aria-pressed",this.open);
}else{
_4.remove(this.focusNode,"aria-controls");
_4.remove(this.focusNode,"tabIndex");
_4.remove(this.focusNode,"aria-pressed");
}
this._set("toggleable",_17);
this._setCss();
},_setContentAttr:function(_18){
if(!this.open||!this._wipeOut||this._wipeOut.status()=="playing"){
this.inherited(arguments);
}else{
if(this._wipeIn&&this._wipeIn.status()=="playing"){
this._wipeIn.stop();
}
_6.setMarginBox(this.wipeNode,{h:_6.getMarginBox(this.wipeNode).h});
this.inherited(arguments);
if(this._wipeIn){
this._wipeIn.play();
}else{
this.hideNode.style.display="";
}
}
},toggle:function(){
this._setOpenAttr(!this.open,true);
},_setCss:function(){
var _19=this.titleBarNode||this.focusNode;
var _1a=this._titleBarClass;
this._titleBarClass=this.baseClass+"Title"+(this.toggleable?"":"Fixed")+(this.open?"Open":"Closed");
_5.replace(_19,this._titleBarClass,_1a||"");
_5.replace(_19,this._titleBarClass.replace("TitlePaneTitle",""),(_1a||"").replace("TitlePaneTitle",""));
this.arrowNodeInner.innerHTML=this.open?"-":"+";
},_onTitleKey:function(e){
if(e.keyCode==_a.DOWN_ARROW&&this.open){
this.containerNode.focus();
e.preventDefault();
}
},_onTitleClick:function(){
if(this.toggleable){
this.toggle();
}
},setTitle:function(_1b){
_9.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.","","2.0");
this.set("title",_1b);
}});
if(_8("dojo-bidi")){
_10.extend({_setTitleAttr:function(_1c){
this._set("title",_1c);
this.titleNode.innerHTML=_1c;
this.applyTextDir(this.titleNode);
},_setTooltipAttr:function(_1d){
this._set("tooltip",_1d);
if(this.textDir){
_1d=this.enforceTextDirWithUcc(null,_1d);
}
_4.set(this.focusNode,"title",_1d);
},_setTextDirAttr:function(_1e){
if(this._created&&this.textDir!=_1e){
this._set("textDir",_1e);
this.set("title",this.title);
this.set("tooltip",this.tooltip);
}
}});
}
return _10;
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set or get domAttr.remove
"dojo/dom-class", // domClass.replace
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.getMarginBox
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/has",
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.DOWN_ARROW keys.ENTER
"./_CssStateMixin",
"./_TemplatedMixin",
"./layout/ContentPane",
"dojo/text!./templates/TitlePane.html",
"./_base/manager", // defaultDuration
"./a11yclick" // template uses ondijitclick
], function(array, declare, dom, domAttr, domClass, domGeometry, fxUtils, has, kernel, keys,
_CssStateMixin, _TemplatedMixin, ContentPane, template, manager){
// module:
// dijit/TitlePane
var TitlePane = declare("dijit.TitlePane", [ContentPane, _TemplatedMixin, _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:
// | <!-- markup href example: -->
// | <div data-dojo-type="dijit/TitlePane" data-dojo-props="href: 'foobar.html', title: 'Title'"></div>
//
// example:
// | <!-- markup with inline data -->
// | <div data-dojo-type="dijit/TitlePane" title="Title">
// | <p>I am content</p>
// | </div>
// title: String
// Title of the pane
title: "",
_setTitleAttr: { node: "titleNode", type: "innerHTML" }, // override default where title becomes a hover tooltip
// 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: manager.defaultDuration,
// baseClass: [protected] String
// The root className to be placed on this widget's domNode.
baseClass: "dijitTitlePane",
templateString: template,
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TitlePane, since TitlePane
// is never a child of a layout container, nor should TitlePane try to control
// the size of an inner widget.
doLayout: false,
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
buildRendering: function(){
this.inherited(arguments);
dom.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, this.baseClass + "Title");
}
// setup open/close animations
var hideNode = this.hideNode, wipeNode = this.wipeNode;
this._wipeIn = fxUtils.wipeIn({
node: wipeNode,
duration: this.duration,
beforeBegin: function(){
hideNode.style.display = "";
}
});
this._wipeOut = fxUtils.wipeOut({
node: 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.
array.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.containerNode.setAttribute("aria-hidden", open ? "false" : "true");
this.focusNode.setAttribute("aria-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.
this.focusNode.setAttribute("role", canToggle ? "button" : "heading");
if(canToggle){
this.focusNode.setAttribute("aria-controls", this.id + "_pane");
this.focusNode.setAttribute("tabIndex", this.tabIndex);
this.focusNode.setAttribute("aria-pressed", this.open);
}else{
domAttr.remove(this.focusNode, "aria-controls");
domAttr.remove(this.focusNode, "tabIndex");
domAttr.remove(this.focusNode, "aria-pressed");
}
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
domGeometry.setMarginBox(this.wipeNode, { h: domGeometry.getMarginBox(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 = this.baseClass + "Title" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
domClass.replace(node, this._titleBarClass, oldCls || "");
// Back compat, remove for 2.0
domClass.replace(node, this._titleBarClass.replace("TitlePaneTitle", ""), (oldCls || "").replace("TitlePaneTitle", ""));
this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
},
_onTitleKey: function(/*Event*/ e){
// summary:
// Handler for when user hits a key
// tags:
// private
if(e.keyCode == 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
kernel.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
this.set("title", title);
}
});
if(has("dojo-bidi")){
TitlePane.extend({
_setTitleAttr: function(/*String*/ title){
// Override default where title becomes a hover tooltip
this._set("title", title);
this.titleNode.innerHTML = title;
this.applyTextDir(this.titleNode);
},
_setTooltipAttr: function(/*String*/ tooltip){
this._set("tooltip", tooltip);
if(this.textDir){
tooltip = this.enforceTextDirWithUcc(null, tooltip);
}
domAttr.set(this.focusNode, "title", tooltip); // focusNode spans the entire width, titleNode doesn't
},
_setTextDirAttr: function(textDir){
if(this._created && this.textDir != textDir){
this._set("textDir", textDir);
this.set("title", this.title);
this.set("tooltip", this.tooltip);
}
}
});
}
return TitlePane;
});
@@ -1,294 +0,0 @@
require({cache:{
'url:dijit/templates/TitlePane.html':"<div>\n\t<div data-dojo-attach-event=\"ondijitclick:_onTitleClick, onkeydown:_onTitleKey\"\n\t\t\tclass=\"dijitTitlePaneTitle\" data-dojo-attach-point=\"titleBarNode\" id=\"${id}_titleBarNode\">\n\t\t<div class=\"dijitTitlePaneTitleFocus\" data-dojo-attach-point=\"focusNode\">\n\t\t\t<span data-dojo-attach-point=\"arrowNode\" class=\"dijitInline dijitArrowNode\" role=\"presentation\"></span\n\t\t\t><span data-dojo-attach-point=\"arrowNodeInner\" class=\"dijitArrowNodeInner\"></span\n\t\t\t><span data-dojo-attach-point=\"titleNode\" class=\"dijitTitlePaneTextNode\"></span>\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTitlePaneContentOuter\" data-dojo-attach-point=\"hideNode\" role=\"presentation\">\n\t\t<div class=\"dijitReset\" data-dojo-attach-point=\"wipeNode\" role=\"presentation\">\n\t\t\t<div class=\"dijitTitlePaneContentInner\" data-dojo-attach-point=\"containerNode\" role=\"region\" id=\"${id}_pane\" aria-labelledby=\"${id}_titleBarNode\">\n\t\t\t\t<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc. Put padding on inner div. -->\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"}});
define("dijit/TitlePane", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set or get domAttr.remove
"dojo/dom-class", // domClass.replace
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.getMarginBox
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/has",
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.DOWN_ARROW keys.ENTER
"./_CssStateMixin",
"./_TemplatedMixin",
"./layout/ContentPane",
"dojo/text!./templates/TitlePane.html",
"./_base/manager", // defaultDuration
"./a11yclick" // template uses ondijitclick
], function(array, declare, dom, domAttr, domClass, domGeometry, fxUtils, has, kernel, keys,
_CssStateMixin, _TemplatedMixin, ContentPane, template, manager){
// module:
// dijit/TitlePane
var TitlePane = declare("dijit.TitlePane", [ContentPane, _TemplatedMixin, _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:
// | <!-- markup href example: -->
// | <div data-dojo-type="dijit/TitlePane" data-dojo-props="href: 'foobar.html', title: 'Title'"></div>
//
// example:
// | <!-- markup with inline data -->
// | <div data-dojo-type="dijit/TitlePane" title="Title">
// | <p>I am content</p>
// | </div>
// title: String
// Title of the pane
title: "",
_setTitleAttr: { node: "titleNode", type: "innerHTML" }, // override default where title becomes a hover tooltip
// 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: manager.defaultDuration,
// baseClass: [protected] String
// The root className to be placed on this widget's domNode.
baseClass: "dijitTitlePane",
templateString: template,
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TitlePane, since TitlePane
// is never a child of a layout container, nor should TitlePane try to control
// the size of an inner widget.
doLayout: false,
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "focusNode", type: "attribute", attribute: "title"}, // focusNode spans the entire width, titleNode doesn't
buildRendering: function(){
this.inherited(arguments);
dom.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, this.baseClass + "Title");
}
// setup open/close animations
var hideNode = this.hideNode, wipeNode = this.wipeNode;
this._wipeIn = fxUtils.wipeIn({
node: wipeNode,
duration: this.duration,
beforeBegin: function(){
hideNode.style.display = "";
}
});
this._wipeOut = fxUtils.wipeOut({
node: 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.
array.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.containerNode.setAttribute("aria-hidden", open ? "false" : "true");
this.focusNode.setAttribute("aria-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.
this.focusNode.setAttribute("role", canToggle ? "button" : "heading");
if(canToggle){
this.focusNode.setAttribute("aria-controls", this.id + "_pane");
this.focusNode.setAttribute("tabIndex", this.tabIndex);
this.focusNode.setAttribute("aria-pressed", this.open);
}else{
domAttr.remove(this.focusNode, "aria-controls");
domAttr.remove(this.focusNode, "tabIndex");
domAttr.remove(this.focusNode, "aria-pressed");
}
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
domGeometry.setMarginBox(this.wipeNode, { h: domGeometry.getMarginBox(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 = this.baseClass + "Title" + (this.toggleable ? "" : "Fixed") + (this.open ? "Open" : "Closed");
domClass.replace(node, this._titleBarClass, oldCls || "");
// Back compat, remove for 2.0
domClass.replace(node, this._titleBarClass.replace("TitlePaneTitle", ""), (oldCls || "").replace("TitlePaneTitle", ""));
this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
},
_onTitleKey: function(/*Event*/ e){
// summary:
// Handler for when user hits a key
// tags:
// private
if(e.keyCode == 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
kernel.deprecated("dijit.TitlePane.setTitle() is deprecated. Use set('title', ...) instead.", "", "2.0");
this.set("title", title);
}
});
if(has("dojo-bidi")){
TitlePane.extend({
_setTitleAttr: function(/*String*/ title){
// Override default where title becomes a hover tooltip
this._set("title", title);
this.titleNode.innerHTML = title;
this.applyTextDir(this.titleNode);
},
_setTooltipAttr: function(/*String*/ tooltip){
this._set("tooltip", tooltip);
if(this.textDir){
tooltip = this.enforceTextDirWithUcc(null, tooltip);
}
domAttr.set(this.focusNode, "title", tooltip); // focusNode spans the entire width, titleNode doesn't
},
_setTextDirAttr: function(textDir){
if(this._created && this.textDir != textDir){
this._set("textDir", textDir);
this.set("title", this.title);
this.set("tooltip", this.tooltip);
}
}
});
}
return TitlePane;
});
@@ -1,14 +1,42 @@
//>>built
define("dijit/Toolbar",["require","dojo/_base/declare","dojo/has","dojo/keys","dojo/ready","./_Widget","./_KeyNavContainer","./_TemplatedMixin"],function(_1,_2,_3,_4,_5,_6,_7,_8){
if(_3("dijit-legacy-requires")){
_5(0,function(){
var _9=["dijit/ToolbarSeparator"];
_1(_9);
});
}
return _2("dijit.Toolbar",[_6,_8,_7],{templateString:"<div class=\"dijit\" role=\"toolbar\" tabIndex=\"${tabIndex}\" data-dojo-attach-point=\"containerNode\">"+"</div>",baseClass:"dijitToolbar",_onLeftArrow:function(){
this.focusPrev();
},_onRightArrow:function(){
this.focusNext();
}});
define([
"require",
"dojo/_base/declare", // declare
"dojo/has",
"dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
"dojo/ready",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(require, declare, has, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/Toolbar
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/ToolbarSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
// summary:
// A Toolbar widget, used to hold things like `dijit/Editor` buttons
templateString:
'<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
'</div>',
baseClass: "dijitToolbar",
_onLeftArrow: function(){
this.focusPrev();
},
_onRightArrow: function(){
this.focusNext();
}
});
});
@@ -1,42 +0,0 @@
define("dijit/Toolbar", [
"require",
"dojo/_base/declare", // declare
"dojo/has",
"dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
"dojo/ready",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(require, declare, has, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/Toolbar
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/ToolbarSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
// summary:
// A Toolbar widget, used to hold things like `dijit/Editor` buttons
templateString:
'<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
'</div>',
baseClass: "dijitToolbar",
_onLeftArrow: function(){
this.focusPrev();
},
_onRightArrow: function(){
this.focusNext();
}
});
});
@@ -1,9 +1,31 @@
//>>built
define("dijit/ToolbarSeparator",["dojo/_base/declare","dojo/dom","./_Widget","./_TemplatedMixin"],function(_1,_2,_3,_4){
return _1("dijit.ToolbarSeparator",[_3,_4],{templateString:"<div class=\"dijitToolbarSeparator dijitInline\" role=\"presentation\"></div>",buildRendering:function(){
this.inherited(arguments);
_2.setSelectable(this.domNode,false);
},isFocusable:function(){
return false;
}});
define([
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_Widget",
"./_TemplatedMixin"
], function(declare, dom, _Widget, _TemplatedMixin){
// module:
// dijit/ToolbarSeparator
return declare("dijit.ToolbarSeparator", [_Widget, _TemplatedMixin], {
// summary:
// A spacer between two `dijit.Toolbar` items
templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// This widget isn't focusable, so pass along that fact.
// tags:
// protected
return false;
}
});
});
@@ -1,31 +0,0 @@
define("dijit/ToolbarSeparator", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_Widget",
"./_TemplatedMixin"
], function(declare, dom, _Widget, _TemplatedMixin){
// module:
// dijit/ToolbarSeparator
return declare("dijit.ToolbarSeparator", [_Widget, _TemplatedMixin], {
// summary:
// A spacer between two `dijit.Toolbar` items
templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// This widget isn't focusable, so pass along that fact.
// tags:
// protected
return false;
}
});
});
@@ -1,240 +1,610 @@
//>>built
require({cache:{"url:dijit/templates/Tooltip.html":"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\" data-dojo-attach-event=\"mouseenter:onMouseEnter,mouseleave:onMouseLeave\"\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n></div>\n"}});
define("dijit/Tooltip",["dojo/_base/array","dojo/_base/declare","dojo/_base/fx","dojo/dom","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/_base/lang","dojo/mouse","dojo/on","dojo/sniff","./_base/manager","./place","./_Widget","./_TemplatedMixin","./BackgroundIframe","dojo/text!./templates/Tooltip.html","./main"],function(_1,_2,fx,_3,_4,_5,_6,_7,_8,on,_9,_a,_b,_c,_d,_e,_f,_10){
var _11=_2("dijit._MasterTooltip",[_c,_d],{duration:_a.defaultDuration,templateString:_f,postCreate:function(){
this.ownerDocumentBody.appendChild(this.domNode);
this.bgIframe=new _e(this.domNode);
this.fadeIn=fx.fadeIn({node:this.domNode,duration:this.duration,onEnd:_7.hitch(this,"_onShow")});
this.fadeOut=fx.fadeOut({node:this.domNode,duration:this.duration,onEnd:_7.hitch(this,"_onHide")});
},show:function(_12,_13,_14,rtl,_15,_16,_17){
if(this.aroundNode&&this.aroundNode===_13&&this.containerNode.innerHTML==_12){
return;
}
if(this.fadeOut.status()=="playing"){
this._onDeck=arguments;
return;
}
this.containerNode.innerHTML=_12;
if(_15){
this.set("textDir",_15);
}
this.containerNode.align=rtl?"right":"left";
var pos=_b.around(this.domNode,_13,_14&&_14.length?_14:_18.defaultPosition,!rtl,_7.hitch(this,"orient"));
var _19=pos.aroundNodePos;
if(pos.corner.charAt(0)=="M"&&pos.aroundCorner.charAt(0)=="M"){
this.connectorNode.style.top=_19.y+((_19.h-this.connectorNode.offsetHeight)>>1)-pos.y+"px";
this.connectorNode.style.left="";
}else{
if(pos.corner.charAt(1)=="M"&&pos.aroundCorner.charAt(1)=="M"){
this.connectorNode.style.left=_19.x+((_19.w-this.connectorNode.offsetWidth)>>1)-pos.x+"px";
}else{
this.connectorNode.style.left="";
this.connectorNode.style.top="";
}
}
_6.set(this.domNode,"opacity",0);
this.fadeIn.play();
this.isShowingNow=true;
this.aroundNode=_13;
this.onMouseEnter=_16||_1a;
this.onMouseLeave=_17||_1a;
},orient:function(_1b,_1c,_1d,_1e,_1f){
this.connectorNode.style.top="";
var _20=_1e.h,_21=_1e.w;
_1b.className="dijitTooltip "+{"MR-ML":"dijitTooltipRight","ML-MR":"dijitTooltipLeft","TM-BM":"dijitTooltipAbove","BM-TM":"dijitTooltipBelow","BL-TL":"dijitTooltipBelow dijitTooltipABLeft","TL-BL":"dijitTooltipAbove dijitTooltipABLeft","BR-TR":"dijitTooltipBelow dijitTooltipABRight","TR-BR":"dijitTooltipAbove dijitTooltipABRight","BR-BL":"dijitTooltipRight","BL-BR":"dijitTooltipLeft"}[_1c+"-"+_1d];
this.domNode.style.width="auto";
var _22=_5.position(this.domNode);
if(_9("ie")||_9("trident")){
_22.w+=2;
}
var _23=Math.min((Math.max(_21,1)),_22.w);
_5.setMarginBox(this.domNode,{w:_23});
if(_1d.charAt(0)=="B"&&_1c.charAt(0)=="B"){
var bb=_5.position(_1b);
var _24=this.connectorNode.offsetHeight;
if(bb.h>_20){
var _25=_20-((_1f.h+_24)>>1);
this.connectorNode.style.top=_25+"px";
this.connectorNode.style.bottom="";
}else{
this.connectorNode.style.bottom=Math.min(Math.max(_1f.h/2-_24/2,0),bb.h-_24)+"px";
this.connectorNode.style.top="";
}
}else{
this.connectorNode.style.top="";
this.connectorNode.style.bottom="";
}
return Math.max(0,_22.w-_21);
},_onShow:function(){
if(_9("ie")){
this.domNode.style.filter="";
}
},hide:function(_26){
if(this._onDeck&&this._onDeck[1]==_26){
this._onDeck=null;
}else{
if(this.aroundNode===_26){
this.fadeIn.stop();
this.isShowingNow=false;
this.aroundNode=null;
this.fadeOut.play();
}else{
}
}
this.onMouseEnter=this.onMouseLeave=_1a;
},_onHide:function(){
this.domNode.style.cssText="";
this.containerNode.innerHTML="";
if(this._onDeck){
this.show.apply(this,this._onDeck);
this._onDeck=null;
}
}});
if(_9("dojo-bidi")){
_11.extend({_setAutoTextDir:function(_27){
this.applyTextDir(_27);
_1.forEach(_27.children,function(_28){
this._setAutoTextDir(_28);
},this);
},_setTextDirAttr:function(_29){
this._set("textDir",_29);
if(_29=="auto"){
this._setAutoTextDir(this.containerNode);
}else{
this.containerNode.dir=this.textDir;
}
}});
}
_10.showTooltip=function(_2a,_2b,_2c,rtl,_2d,_2e,_2f){
if(_2c){
_2c=_1.map(_2c,function(val){
return {after:"after-centered",before:"before-centered"}[val]||val;
});
}
if(!_18._masterTT){
_10._masterTT=_18._masterTT=new _11();
}
return _18._masterTT.show(_2a,_2b,_2c,rtl,_2d,_2e,_2f);
};
_10.hideTooltip=function(_30){
return _18._masterTT&&_18._masterTT.hide(_30);
};
var _31="DORMANT",_32="SHOW TIMER",_33="SHOWING",_34="HIDE TIMER";
function _1a(){
};
var _18=_2("dijit.Tooltip",_c,{label:"",showDelay:400,hideDelay:400,connectId:[],position:[],selector:"",_setConnectIdAttr:function(_35){
_1.forEach(this._connections||[],function(_36){
_1.forEach(_36,function(_37){
_37.remove();
});
},this);
this._connectIds=_1.filter(_7.isArrayLike(_35)?_35:(_35?[_35]:[]),function(id){
return _3.byId(id,this.ownerDocument);
},this);
this._connections=_1.map(this._connectIds,function(id){
var _38=_3.byId(id,this.ownerDocument),_39=this.selector,_3a=_39?function(_3b){
return on.selector(_39,_3b);
}:function(_3c){
return _3c;
},_3d=this;
return [on(_38,_3a(_8.enter),function(){
_3d._onHover(this);
}),on(_38,_3a("focusin"),function(){
_3d._onHover(this);
}),on(_38,_3a(_8.leave),_7.hitch(_3d,"_onUnHover")),on(_38,_3a("focusout"),_7.hitch(_3d,"set","state",_31))];
},this);
this._set("connectId",_35);
},addTarget:function(_3e){
var id=_3e.id||_3e;
if(_1.indexOf(this._connectIds,id)==-1){
this.set("connectId",this._connectIds.concat(id));
}
},removeTarget:function(_3f){
var id=_3f.id||_3f,idx=_1.indexOf(this._connectIds,id);
if(idx>=0){
this._connectIds.splice(idx,1);
this.set("connectId",this._connectIds);
}
},buildRendering:function(){
this.inherited(arguments);
_4.add(this.domNode,"dijitTooltipData");
},startup:function(){
this.inherited(arguments);
var ids=this.connectId;
_1.forEach(_7.isArrayLike(ids)?ids:[ids],this.addTarget,this);
},getContent:function(_40){
return this.label||this.domNode.innerHTML;
},state:_31,_setStateAttr:function(val){
if(this.state==val||(val==_32&&this.state==_33)||(val==_34&&this.state==_31)){
return;
}
if(this._hideTimer){
this._hideTimer.remove();
delete this._hideTimer;
}
if(this._showTimer){
this._showTimer.remove();
delete this._showTimer;
}
switch(val){
case _31:
if(this._connectNode){
_18.hide(this._connectNode);
delete this._connectNode;
this.onHide();
}
break;
case _32:
if(this.state!=_33){
this._showTimer=this.defer(function(){
this.set("state",_33);
},this.showDelay);
}
break;
case _33:
var _41=this.getContent(this._connectNode);
if(!_41){
this.set("state",_31);
return;
}
_18.show(_41,this._connectNode,this.position,!this.isLeftToRight(),this.textDir,_7.hitch(this,"set","state",_33),_7.hitch(this,"set","state",_34));
this.onShow(this._connectNode,this.position);
break;
case _34:
this._hideTimer=this.defer(function(){
this.set("state",_31);
},this.hideDelay);
break;
}
this._set("state",val);
},_onHover:function(_42){
if(this._connectNode&&_42!=this._connectNode){
this.set("state",_31);
}
this._connectNode=_42;
this.set("state",_32);
},_onUnHover:function(_43){
this.set("state",_34);
},open:function(_44){
this.set("state",_31);
this._connectNode=_44;
this.set("state",_33);
},close:function(){
this.set("state",_31);
},onShow:function(){
},onHide:function(){
},destroy:function(){
this.set("state",_31);
_1.forEach(this._connections||[],function(_45){
_1.forEach(_45,function(_46){
_46.remove();
});
},this);
this.inherited(arguments);
}});
_18._MasterTooltip=_11;
_18.show=_10.showTooltip;
_18.hide=_10.hideTooltip;
_18.defaultPosition=["after-centered","before-centered"];
return _18;
define([
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/dom", // dom.byId
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/_base/lang", // lang.hitch lang.isArrayLike
"dojo/mouse",
"dojo/on",
"dojo/sniff", // has("ie"), has("trident")
"./_base/manager", // manager.defaultDuration
"./place",
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"dojo/text!./templates/Tooltip.html",
"./main" // sets dijit.showTooltip etc. for back-compat
], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, mouse, on, has,
manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
// module:
// dijit/Tooltip
// TODO: Tooltip should really share more positioning code with TooltipDialog, like:
// - the orient() method
// - the connector positioning code in show()
// - the dijitTooltip[Dialog] class
//
// The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
// with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
// summary:
// Internal widget that holds the actual tooltip markup,
// which occurs once per page.
// Called by Tooltip widgets which are just containers to hold
// the markup
// tags:
// protected
// duration: Integer
// Milliseconds to fade in/fade out
duration: manager.defaultDuration,
templateString: template,
postCreate: function(){
this.ownerDocumentBody.appendChild(this.domNode);
this.bgIframe = new BackgroundIframe(this.domNode);
// Setup fade-in and fade-out functions.
this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
},
show: function(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave){
// summary:
// Display tooltip w/specified contents to right of specified node
// (To left if there's no space on the right, or if rtl == true)
// innerHTML: String
// Contents of the tooltip
// aroundNode: DomNode|dijit/place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
// onMouseEnter: Function?
// Callback function for mouse enter on tooltip
// onMouseLeave: Function?
// Callback function for mouse leave on tooltip
if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
return;
}
if(this.fadeOut.status() == "playing"){
// previous tooltip is being hidden; wait until the hide completes then show new one
this._onDeck=arguments;
return;
}
this.containerNode.innerHTML=innerHTML;
if(textDir){
this.set("textDir", textDir);
}
this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
var pos = place.around(this.domNode, aroundNode,
position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}else{
// Not *-centered, but just above/below/after/before
this.connectorNode.style.left = "";
this.connectorNode.style.top = "";
}
// show it
domStyle.set(this.domNode, "opacity", 0);
this.fadeIn.play();
this.isShowingNow = true;
this.aroundNode = aroundNode;
this.onMouseEnter = onMouseEnter || noop;
this.onMouseLeave = onMouseLeave || noop;
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
// summary:
// Private function to set CSS for tooltip node based on which position it's in.
// This is called by the dijit popup code. It will also reduce the tooltip's
// width to whatever width is available
// tags:
// protected
this.connectorNode.style.top = ""; //reset to default
var heightAvailable = spaceAvailable.h,
widthAvailable = spaceAvailable.w;
node.className = "dijitTooltip " +
{
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft"
}[aroundCorner + "-" + tooltipCorner];
// reset width; it may have been set by orient() on a previous tooltip show()
this.domNode.style.width = "auto";
// Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
// Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
// negative number since that causes an exception on IE.
var size = domGeometry.position(this.domNode);
if(has("ie") || has("trident")){
// workaround strange IE bug where setting width to offsetWidth causes words to wrap
size.w += 2;
}
var width = Math.min((Math.max(widthAvailable,1)), size.w);
domGeometry.setMarginBox(this.domNode, {w: width});
// Reposition the tooltip connector.
if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
var bb = domGeometry.position(node);
var tooltipConnectorHeight = this.connectorNode.offsetHeight;
if(bb.h > heightAvailable){
// The tooltip starts at the top of the page and will extend past the aroundNode
var aroundNodePlacement = heightAvailable - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
this.connectorNode.style.top = aroundNodePlacement + "px";
this.connectorNode.style.bottom = "";
}else{
// Align center of connector with center of aroundNode, except don't let bottom
// of connector extend below bottom of tooltip content, or top of connector
// extend past top of tooltip content
this.connectorNode.style.bottom = Math.min(
Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
bb.h - tooltipConnectorHeight) + "px";
this.connectorNode.style.top = "";
}
}else{
// reset the tooltip back to the defaults
this.connectorNode.style.top = "";
this.connectorNode.style.bottom = "";
}
return Math.max(0, size.w - widthAvailable);
},
_onShow: function(){
// summary:
// Called at end of fade-in operation
// tags:
// protected
if(has("ie")){
// the arrow won't show up on a node w/an opacity filter
this.domNode.style.filter="";
}
},
hide: function(aroundNode){
// summary:
// Hide the tooltip
if(this._onDeck && this._onDeck[1] == aroundNode){
// this hide request is for a show() that hasn't even started yet;
// just cancel the pending show()
this._onDeck=null;
}else if(this.aroundNode === aroundNode){
// this hide request is for the currently displayed tooltip
this.fadeIn.stop();
this.isShowingNow = false;
this.aroundNode = null;
this.fadeOut.play();
}else{
// just ignore the call, it's for a tooltip that has already been erased
}
this.onMouseEnter = this.onMouseLeave = noop;
},
_onHide: function(){
// summary:
// Called at end of fade-out operation
// tags:
// protected
this.domNode.style.cssText=""; // to position offscreen again
this.containerNode.innerHTML="";
if(this._onDeck){
// a show request has been queued up; do it now
this.show.apply(this, this._onDeck);
this._onDeck=null;
}
}
});
if(has("dojo-bidi")){
MasterTooltip.extend({
_setAutoTextDir: function(/*Object*/node){
// summary:
// Resolve "auto" text direction for children nodes
// tags:
// private
this.applyTextDir(node);
array.forEach(node.children, function(child){ this._setAutoTextDir(child); }, this);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this._set("textDir", textDir);
if (textDir == "auto"){
this._setAutoTextDir(this.containerNode);
}else{
this.containerNode.dir = this.textDir;
}
}
});
}
dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave){
// summary:
// Static method to display tooltip w/specified contents in specified position.
// See description of dijit/Tooltip.defaultPosition for details on position parameter.
// If position is not specified then dijit/Tooltip.defaultPosition is used.
// innerHTML: String
// Contents of the tooltip
// aroundNode: place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
// onMouseEnter: Function?
// Callback function for mouse over on tooltip
// onMouseLeave: Function?
// Callback function for mouse leave on tooltip
// After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
// Possibly remove this in 2.0. Alternately, get before/after to work.
if(position){
position = array.map(position, function(val){
return {after: "after-centered", before: "before-centered"}[val] || val;
});
}
if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave);
};
dijit.hideTooltip = function(aroundNode){
// summary:
// Static method to hide the tooltip displayed via showTooltip()
return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
};
// Possible states for a tooltip, see Tooltip.state property for definitions
var DORMANT = "DORMANT",
SHOW_TIMER = "SHOW TIMER",
SHOWING = "SHOWING",
HIDE_TIMER = "HIDE TIMER";
function noop(){}
var Tooltip = declare("dijit.Tooltip", _Widget, {
// summary:
// Pops up a tooltip (a help message) when you hover over a node.
// Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
// label: String
// HTML to display in the tooltip.
// Specified as innerHTML when creating the widget from markup.
label: "",
// showDelay: Integer
// Number of milliseconds to wait after hovering over/focusing on the object, before
// the tooltip is displayed.
showDelay: 400,
// hideDelay: Integer
// Number of milliseconds to wait after unhovering the object, before
// the tooltip is hidden. Note that blurring an object hides the tooltip immediately.
hideDelay: 400,
// connectId: String|String[]|DomNode|DomNode[]
// Id of domNode(s) to attach the tooltip to.
// When user hovers over specified dom node(s), the tooltip will appear.
connectId: [],
// position: String[]
// See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
position: [],
// selector: String?
// CSS expression to apply this Tooltip to descendants of connectIds, rather than to
// the nodes specified by connectIds themselves. Useful for applying a Tooltip to
// a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
// Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
// So, change connectId to a "", remove addTarget()/removeTarget(), etc.
_setConnectIdAttr: function(/*String|String[]|DomNode|DomNode[]*/ newId){
// summary:
// Connect to specified node(s)
// Remove connections to old nodes (if there are any)
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
// Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
function(id){ return dom.byId(id, this.ownerDocument); }, this);
// Make connections
this._connections = array.map(this._connectIds, function(id){
var node = dom.byId(id, this.ownerDocument),
selector = this.selector,
delegatedEvent = selector ?
function(eventType){ return on.selector(selector, eventType); } :
function(eventType){ return eventType; },
self = this;
return [
on(node, delegatedEvent(mouse.enter), function(){
self._onHover(this);
}),
on(node, delegatedEvent("focusin"), function(){
self._onHover(this);
}),
on(node, delegatedEvent(mouse.leave), lang.hitch(self, "_onUnHover")),
on(node, delegatedEvent("focusout"), lang.hitch(self, "set", "state", DORMANT))
];
}, this);
this._set("connectId", newId);
},
addTarget: function(/*OomNode|String*/ node){
// summary:
// Attach tooltip to specified node if it's not already connected
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node;
if(array.indexOf(this._connectIds, id) == -1){
this.set("connectId", this._connectIds.concat(id));
}
},
removeTarget: function(/*DomNode|String*/ node){
// summary:
// Detach tooltip from specified node
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node, // map from DOMNode back to plain id string
idx = array.indexOf(this._connectIds, id);
if(idx >= 0){
// remove id (modifies original this._connectIds but that's OK in this case)
this._connectIds.splice(idx, 1);
this.set("connectId", this._connectIds);
}
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode,"dijitTooltipData");
},
startup: function(){
this.inherited(arguments);
// If this tooltip was created in a template, or for some other reason the specified connectId[s]
// didn't exist during the widget's initialization, then connect now.
var ids = this.connectId;
array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
},
getContent: function(/*DomNode*/ node){
// summary:
// User overridable function that return the text to display in the tooltip.
// tags:
// extension
return this.label || this.domNode.innerHTML;
},
// state: [private readonly] String
// One of:
//
// - DORMANT: tooltip not SHOWING
// - SHOW TIMER: tooltip not SHOWING but timer set to show it
// - SHOWING: tooltip displayed
// - HIDE TIMER: tooltip displayed, but timer set to hide it
state: DORMANT,
_setStateAttr: function(val){
if(this.state == val ||
(val == SHOW_TIMER && this.state == SHOWING) ||
(val == HIDE_TIMER && this.state == DORMANT)){
return;
}
if(this._hideTimer){
this._hideTimer.remove();
delete this._hideTimer;
}
if(this._showTimer){
this._showTimer.remove();
delete this._showTimer;
}
switch(val){
case DORMANT:
if(this._connectNode){
Tooltip.hide(this._connectNode);
delete this._connectNode;
this.onHide();
}
break;
case SHOW_TIMER: // set timer to show tooltip
// should only get here from a DORMANT state, i.e. tooltip can't be already SHOWING
if(this.state != SHOWING){
this._showTimer = this.defer(function(){ this.set("state", SHOWING); }, this.showDelay);
}
break;
case SHOWING: // show tooltip and clear timers
var content = this.getContent(this._connectNode);
if(!content){
this.set("state", DORMANT);
return;
}
// Show tooltip and setup callbacks for mouseenter/mouseleave of tooltip itself
Tooltip.show(content, this._connectNode, this.position, !this.isLeftToRight(), this.textDir,
lang.hitch(this, "set", "state", SHOWING), lang.hitch(this, "set", "state", HIDE_TIMER));
this.onShow(this._connectNode, this.position);
break;
case HIDE_TIMER: // set timer set to hide tooltip
this._hideTimer = this.defer(function(){ this.set("state", DORMANT); }, this.hideDelay);
break;
}
this._set("state", val);
},
_onHover: function(/*DomNode*/ target){
// summary:
// Despite the name of this method, it actually handles both hover and focus
// events on the target node, setting a timer to show the tooltip.
// tags:
// private
if(this._connectNode && target != this._connectNode){
// Tooltip is displaying for another node
this.set("state", DORMANT);
}
this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
this.set("state", SHOW_TIMER); // no-op if show-timer already set, or if already showing
},
_onUnHover: function(/*DomNode*/ target){
// summary:
// Handles mouseleave event on the target node, hiding the tooltip.
// tags:
// private
this.set("state", HIDE_TIMER); // no-op if already dormant, or if hide-timer already set
},
// open() and close() aren't used anymore, except from the _BidiSupport/misc/Tooltip test.
// Should probably remove for 2.0, but leaving for now.
open: function(/*DomNode*/ target){
// summary:
// Display the tooltip; usually not called directly.
// tags:
// private
this.set("state", DORMANT);
this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
this.set("state", SHOWING);
},
close: function(){
// summary:
// Hide the tooltip or cancel timer for show of tooltip
// tags:
// private
this.set("state", DORMANT);
},
onShow: function(/*===== target, position =====*/){
// summary:
// Called when the tooltip is shown
// tags:
// callback
},
onHide: function(){
// summary:
// Called when the tooltip is hidden
// tags:
// callback
},
destroy: function(){
this.set("state", DORMANT);
// Remove connections manually since they aren't registered to be removed by _WidgetBase
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
this.inherited(arguments);
}
});
Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
Tooltip.show = dijit.showTooltip; // export function through module return value
Tooltip.hide = dijit.hideTooltip; // export function through module return value
Tooltip.defaultPosition = ["after-centered", "before-centered"];
/*=====
lang.mixin(Tooltip, {
// defaultPosition: String[]
// This variable controls the position of tooltips, if the position is not specified to
// the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
// possible for `dijit/place.around()`. The recommended values are:
//
// - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
// in the case of RTL scripts like Hebrew and Arabic
// - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
// in the case of RTL scripts like Hebrew and Arabic
// - above-centered: tooltip is centered above anchor node
// - below-centered: tooltip is centered above anchor node
//
// The list is positions is tried, in order, until a position is found where the tooltip fits
// within the viewport.
//
// Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
// the screen so that there's no room above the target node. Nodes with drop downs, like
// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
// is only room below (or above) the target node, but not both.
});
=====*/
return Tooltip;
});
@@ -1,612 +0,0 @@
require({cache:{
'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\" data-dojo-attach-event=\"mouseenter:onMouseEnter,mouseleave:onMouseLeave\"\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n></div>\n"}});
define("dijit/Tooltip", [
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/dom", // dom.byId
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/_base/lang", // lang.hitch lang.isArrayLike
"dojo/mouse",
"dojo/on",
"dojo/sniff", // has("ie"), has("trident")
"./_base/manager", // manager.defaultDuration
"./place",
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"dojo/text!./templates/Tooltip.html",
"./main" // sets dijit.showTooltip etc. for back-compat
], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, mouse, on, has,
manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
// module:
// dijit/Tooltip
// TODO: Tooltip should really share more positioning code with TooltipDialog, like:
// - the orient() method
// - the connector positioning code in show()
// - the dijitTooltip[Dialog] class
//
// The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
// with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
// summary:
// Internal widget that holds the actual tooltip markup,
// which occurs once per page.
// Called by Tooltip widgets which are just containers to hold
// the markup
// tags:
// protected
// duration: Integer
// Milliseconds to fade in/fade out
duration: manager.defaultDuration,
templateString: template,
postCreate: function(){
this.ownerDocumentBody.appendChild(this.domNode);
this.bgIframe = new BackgroundIframe(this.domNode);
// Setup fade-in and fade-out functions.
this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
},
show: function(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave){
// summary:
// Display tooltip w/specified contents to right of specified node
// (To left if there's no space on the right, or if rtl == true)
// innerHTML: String
// Contents of the tooltip
// aroundNode: DomNode|dijit/place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
// onMouseEnter: Function?
// Callback function for mouse enter on tooltip
// onMouseLeave: Function?
// Callback function for mouse leave on tooltip
if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
return;
}
if(this.fadeOut.status() == "playing"){
// previous tooltip is being hidden; wait until the hide completes then show new one
this._onDeck=arguments;
return;
}
this.containerNode.innerHTML=innerHTML;
if(textDir){
this.set("textDir", textDir);
}
this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
var pos = place.around(this.domNode, aroundNode,
position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}else{
// Not *-centered, but just above/below/after/before
this.connectorNode.style.left = "";
this.connectorNode.style.top = "";
}
// show it
domStyle.set(this.domNode, "opacity", 0);
this.fadeIn.play();
this.isShowingNow = true;
this.aroundNode = aroundNode;
this.onMouseEnter = onMouseEnter || noop;
this.onMouseLeave = onMouseLeave || noop;
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
// summary:
// Private function to set CSS for tooltip node based on which position it's in.
// This is called by the dijit popup code. It will also reduce the tooltip's
// width to whatever width is available
// tags:
// protected
this.connectorNode.style.top = ""; //reset to default
var heightAvailable = spaceAvailable.h,
widthAvailable = spaceAvailable.w;
node.className = "dijitTooltip " +
{
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft"
}[aroundCorner + "-" + tooltipCorner];
// reset width; it may have been set by orient() on a previous tooltip show()
this.domNode.style.width = "auto";
// Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
// Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
// negative number since that causes an exception on IE.
var size = domGeometry.position(this.domNode);
if(has("ie") || has("trident")){
// workaround strange IE bug where setting width to offsetWidth causes words to wrap
size.w += 2;
}
var width = Math.min((Math.max(widthAvailable,1)), size.w);
domGeometry.setMarginBox(this.domNode, {w: width});
// Reposition the tooltip connector.
if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
var bb = domGeometry.position(node);
var tooltipConnectorHeight = this.connectorNode.offsetHeight;
if(bb.h > heightAvailable){
// The tooltip starts at the top of the page and will extend past the aroundNode
var aroundNodePlacement = heightAvailable - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
this.connectorNode.style.top = aroundNodePlacement + "px";
this.connectorNode.style.bottom = "";
}else{
// Align center of connector with center of aroundNode, except don't let bottom
// of connector extend below bottom of tooltip content, or top of connector
// extend past top of tooltip content
this.connectorNode.style.bottom = Math.min(
Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
bb.h - tooltipConnectorHeight) + "px";
this.connectorNode.style.top = "";
}
}else{
// reset the tooltip back to the defaults
this.connectorNode.style.top = "";
this.connectorNode.style.bottom = "";
}
return Math.max(0, size.w - widthAvailable);
},
_onShow: function(){
// summary:
// Called at end of fade-in operation
// tags:
// protected
if(has("ie")){
// the arrow won't show up on a node w/an opacity filter
this.domNode.style.filter="";
}
},
hide: function(aroundNode){
// summary:
// Hide the tooltip
if(this._onDeck && this._onDeck[1] == aroundNode){
// this hide request is for a show() that hasn't even started yet;
// just cancel the pending show()
this._onDeck=null;
}else if(this.aroundNode === aroundNode){
// this hide request is for the currently displayed tooltip
this.fadeIn.stop();
this.isShowingNow = false;
this.aroundNode = null;
this.fadeOut.play();
}else{
// just ignore the call, it's for a tooltip that has already been erased
}
this.onMouseEnter = this.onMouseLeave = noop;
},
_onHide: function(){
// summary:
// Called at end of fade-out operation
// tags:
// protected
this.domNode.style.cssText=""; // to position offscreen again
this.containerNode.innerHTML="";
if(this._onDeck){
// a show request has been queued up; do it now
this.show.apply(this, this._onDeck);
this._onDeck=null;
}
}
});
if(has("dojo-bidi")){
MasterTooltip.extend({
_setAutoTextDir: function(/*Object*/node){
// summary:
// Resolve "auto" text direction for children nodes
// tags:
// private
this.applyTextDir(node);
array.forEach(node.children, function(child){ this._setAutoTextDir(child); }, this);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this._set("textDir", textDir);
if (textDir == "auto"){
this._setAutoTextDir(this.containerNode);
}else{
this.containerNode.dir = this.textDir;
}
}
});
}
dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave){
// summary:
// Static method to display tooltip w/specified contents in specified position.
// See description of dijit/Tooltip.defaultPosition for details on position parameter.
// If position is not specified then dijit/Tooltip.defaultPosition is used.
// innerHTML: String
// Contents of the tooltip
// aroundNode: place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
// onMouseEnter: Function?
// Callback function for mouse over on tooltip
// onMouseLeave: Function?
// Callback function for mouse leave on tooltip
// After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
// Possibly remove this in 2.0. Alternately, get before/after to work.
if(position){
position = array.map(position, function(val){
return {after: "after-centered", before: "before-centered"}[val] || val;
});
}
if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir, onMouseEnter, onMouseLeave);
};
dijit.hideTooltip = function(aroundNode){
// summary:
// Static method to hide the tooltip displayed via showTooltip()
return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
};
// Possible states for a tooltip, see Tooltip.state property for definitions
var DORMANT = "DORMANT",
SHOW_TIMER = "SHOW TIMER",
SHOWING = "SHOWING",
HIDE_TIMER = "HIDE TIMER";
function noop(){}
var Tooltip = declare("dijit.Tooltip", _Widget, {
// summary:
// Pops up a tooltip (a help message) when you hover over a node.
// Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
// label: String
// HTML to display in the tooltip.
// Specified as innerHTML when creating the widget from markup.
label: "",
// showDelay: Integer
// Number of milliseconds to wait after hovering over/focusing on the object, before
// the tooltip is displayed.
showDelay: 400,
// hideDelay: Integer
// Number of milliseconds to wait after unhovering the object, before
// the tooltip is hidden. Note that blurring an object hides the tooltip immediately.
hideDelay: 400,
// connectId: String|String[]|DomNode|DomNode[]
// Id of domNode(s) to attach the tooltip to.
// When user hovers over specified dom node(s), the tooltip will appear.
connectId: [],
// position: String[]
// See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
position: [],
// selector: String?
// CSS expression to apply this Tooltip to descendants of connectIds, rather than to
// the nodes specified by connectIds themselves. Useful for applying a Tooltip to
// a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
// Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
// So, change connectId to a "", remove addTarget()/removeTarget(), etc.
_setConnectIdAttr: function(/*String|String[]|DomNode|DomNode[]*/ newId){
// summary:
// Connect to specified node(s)
// Remove connections to old nodes (if there are any)
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
// Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
function(id){ return dom.byId(id, this.ownerDocument); }, this);
// Make connections
this._connections = array.map(this._connectIds, function(id){
var node = dom.byId(id, this.ownerDocument),
selector = this.selector,
delegatedEvent = selector ?
function(eventType){ return on.selector(selector, eventType); } :
function(eventType){ return eventType; },
self = this;
return [
on(node, delegatedEvent(mouse.enter), function(){
self._onHover(this);
}),
on(node, delegatedEvent("focusin"), function(){
self._onHover(this);
}),
on(node, delegatedEvent(mouse.leave), lang.hitch(self, "_onUnHover")),
on(node, delegatedEvent("focusout"), lang.hitch(self, "set", "state", DORMANT))
];
}, this);
this._set("connectId", newId);
},
addTarget: function(/*OomNode|String*/ node){
// summary:
// Attach tooltip to specified node if it's not already connected
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node;
if(array.indexOf(this._connectIds, id) == -1){
this.set("connectId", this._connectIds.concat(id));
}
},
removeTarget: function(/*DomNode|String*/ node){
// summary:
// Detach tooltip from specified node
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node, // map from DOMNode back to plain id string
idx = array.indexOf(this._connectIds, id);
if(idx >= 0){
// remove id (modifies original this._connectIds but that's OK in this case)
this._connectIds.splice(idx, 1);
this.set("connectId", this._connectIds);
}
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode,"dijitTooltipData");
},
startup: function(){
this.inherited(arguments);
// If this tooltip was created in a template, or for some other reason the specified connectId[s]
// didn't exist during the widget's initialization, then connect now.
var ids = this.connectId;
array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
},
getContent: function(/*DomNode*/ node){
// summary:
// User overridable function that return the text to display in the tooltip.
// tags:
// extension
return this.label || this.domNode.innerHTML;
},
// state: [private readonly] String
// One of:
//
// - DORMANT: tooltip not SHOWING
// - SHOW TIMER: tooltip not SHOWING but timer set to show it
// - SHOWING: tooltip displayed
// - HIDE TIMER: tooltip displayed, but timer set to hide it
state: DORMANT,
_setStateAttr: function(val){
if(this.state == val ||
(val == SHOW_TIMER && this.state == SHOWING) ||
(val == HIDE_TIMER && this.state == DORMANT)){
return;
}
if(this._hideTimer){
this._hideTimer.remove();
delete this._hideTimer;
}
if(this._showTimer){
this._showTimer.remove();
delete this._showTimer;
}
switch(val){
case DORMANT:
if(this._connectNode){
Tooltip.hide(this._connectNode);
delete this._connectNode;
this.onHide();
}
break;
case SHOW_TIMER: // set timer to show tooltip
// should only get here from a DORMANT state, i.e. tooltip can't be already SHOWING
if(this.state != SHOWING){
this._showTimer = this.defer(function(){ this.set("state", SHOWING); }, this.showDelay);
}
break;
case SHOWING: // show tooltip and clear timers
var content = this.getContent(this._connectNode);
if(!content){
this.set("state", DORMANT);
return;
}
// Show tooltip and setup callbacks for mouseenter/mouseleave of tooltip itself
Tooltip.show(content, this._connectNode, this.position, !this.isLeftToRight(), this.textDir,
lang.hitch(this, "set", "state", SHOWING), lang.hitch(this, "set", "state", HIDE_TIMER));
this.onShow(this._connectNode, this.position);
break;
case HIDE_TIMER: // set timer set to hide tooltip
this._hideTimer = this.defer(function(){ this.set("state", DORMANT); }, this.hideDelay);
break;
}
this._set("state", val);
},
_onHover: function(/*DomNode*/ target){
// summary:
// Despite the name of this method, it actually handles both hover and focus
// events on the target node, setting a timer to show the tooltip.
// tags:
// private
if(this._connectNode && target != this._connectNode){
// Tooltip is displaying for another node
this.set("state", DORMANT);
}
this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
this.set("state", SHOW_TIMER); // no-op if show-timer already set, or if already showing
},
_onUnHover: function(/*DomNode*/ target){
// summary:
// Handles mouseleave event on the target node, hiding the tooltip.
// tags:
// private
this.set("state", HIDE_TIMER); // no-op if already dormant, or if hide-timer already set
},
// open() and close() aren't used anymore, except from the _BidiSupport/misc/Tooltip test.
// Should probably remove for 2.0, but leaving for now.
open: function(/*DomNode*/ target){
// summary:
// Display the tooltip; usually not called directly.
// tags:
// private
this.set("state", DORMANT);
this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
this.set("state", SHOWING);
},
close: function(){
// summary:
// Hide the tooltip or cancel timer for show of tooltip
// tags:
// private
this.set("state", DORMANT);
},
onShow: function(/*===== target, position =====*/){
// summary:
// Called when the tooltip is shown
// tags:
// callback
},
onHide: function(){
// summary:
// Called when the tooltip is hidden
// tags:
// callback
},
destroy: function(){
this.set("state", DORMANT);
// Remove connections manually since they aren't registered to be removed by _WidgetBase
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
this.inherited(arguments);
}
});
Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
Tooltip.show = dijit.showTooltip; // export function through module return value
Tooltip.hide = dijit.hideTooltip; // export function through module return value
Tooltip.defaultPosition = ["after-centered", "before-centered"];
/*=====
lang.mixin(Tooltip, {
// defaultPosition: String[]
// This variable controls the position of tooltips, if the position is not specified to
// the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
// possible for `dijit/place.around()`. The recommended values are:
//
// - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
// in the case of RTL scripts like Hebrew and Arabic
// - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
// in the case of RTL scripts like Hebrew and Arabic
// - above-centered: tooltip is centered above anchor node
// - below-centered: tooltip is centered above anchor node
//
// The list is positions is tried, in order, until a position is found where the tooltip fits
// within the viewport.
//
// Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
// the screen so that there's no room above the target node. Nodes with drop downs, like
// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
// is only room below (or above) the target node, but not both.
});
=====*/
return Tooltip;
});
@@ -1,72 +1,198 @@
//>>built
require({cache:{"url:dijit/templates/TooltipDialog.html":"<div role=\"alertdialog\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"contentsNode\" class=\"dijitTooltipContents dijitTooltipFocusNode\">\n\t\t\t<div data-dojo-attach-point=\"containerNode\"></div>\n\t\t\t${!actionBarTemplate}\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n"}});
define("dijit/TooltipDialog",["dojo/_base/declare","dojo/dom-class","dojo/has","dojo/keys","dojo/_base/lang","dojo/on","./focus","./layout/ContentPane","./_DialogMixin","./form/_FormMixin","./_TemplatedMixin","dojo/text!./templates/TooltipDialog.html","./main"],function(_1,_2,_3,_4,_5,on,_6,_7,_8,_9,_a,_b,_c){
var _d=_1("dijit.TooltipDialog",[_7,_a,_9,_8],{title:"",doLayout:false,autofocus:true,baseClass:"dijitTooltipDialog",_firstFocusItem:null,_lastFocusItem:null,templateString:_b,_setTitleAttr:"containerNode",postCreate:function(){
this.inherited(arguments);
this.own(on(this.domNode,"keydown",_5.hitch(this,"_onKey")));
},orient:function(_e,_f,_10){
var _11={"MR-ML":"dijitTooltipRight","ML-MR":"dijitTooltipLeft","TM-BM":"dijitTooltipAbove","BM-TM":"dijitTooltipBelow","BL-TL":"dijitTooltipBelow dijitTooltipABLeft","TL-BL":"dijitTooltipAbove dijitTooltipABLeft","BR-TR":"dijitTooltipBelow dijitTooltipABRight","TR-BR":"dijitTooltipAbove dijitTooltipABRight","BR-BL":"dijitTooltipRight","BL-BR":"dijitTooltipLeft","BR-TL":"dijitTooltipBelow dijitTooltipABLeft","BL-TR":"dijitTooltipBelow dijitTooltipABRight","TL-BR":"dijitTooltipAbove dijitTooltipABRight","TR-BL":"dijitTooltipAbove dijitTooltipABLeft"}[_f+"-"+_10];
_2.replace(this.domNode,_11,this._currentOrientClass||"");
this._currentOrientClass=_11;
},focus:function(){
this._getFocusItems();
_6.focus(this._firstFocusItem);
},onOpen:function(pos){
this.orient(this.domNode,pos.aroundCorner,pos.corner);
var _12=pos.aroundNodePos;
if(pos.corner.charAt(0)=="M"&&pos.aroundCorner.charAt(0)=="M"){
this.connectorNode.style.top=_12.y+((_12.h-this.connectorNode.offsetHeight)>>1)-pos.y+"px";
this.connectorNode.style.left="";
}else{
if(pos.corner.charAt(1)=="M"&&pos.aroundCorner.charAt(1)=="M"){
this.connectorNode.style.left=_12.x+((_12.w-this.connectorNode.offsetWidth)>>1)-pos.x+"px";
}
}
this._onShow();
},onClose:function(){
this.onHide();
},_onKey:function(evt){
if(evt.keyCode==_4.ESCAPE){
this.defer("onCancel");
evt.stopPropagation();
evt.preventDefault();
}else{
if(evt.keyCode==_4.TAB){
var _13=evt.target;
this._getFocusItems();
if(this._firstFocusItem==this._lastFocusItem){
evt.stopPropagation();
evt.preventDefault();
}else{
if(_13==this._firstFocusItem&&evt.shiftKey){
_6.focus(this._lastFocusItem);
evt.stopPropagation();
evt.preventDefault();
}else{
if(_13==this._lastFocusItem&&!evt.shiftKey){
_6.focus(this._firstFocusItem);
evt.stopPropagation();
evt.preventDefault();
}else{
evt.stopPropagation();
}
}
}
}
}
}});
if(_3("dojo-bidi")){
_d.extend({_setTitleAttr:function(_14){
this.containerNode.title=(this.textDir&&this.enforceTextDirWithUcc)?this.enforceTextDirWithUcc(null,_14):_14;
this._set("title",_14);
},_setTextDirAttr:function(_15){
if(!this._created||this.textDir!=_15){
this._set("textDir",_15);
if(this.textDir&&this.title){
this.containerNode.title=this.enforceTextDirWithUcc(null,this.title);
}
}
}});
}
return _d;
define([
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.replace
"dojo/has",
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"dojo/on",
"./focus",
"./layout/ContentPane",
"./_DialogMixin",
"./form/_FormMixin",
"./_TemplatedMixin",
"dojo/text!./templates/TooltipDialog.html",
"./main" // exports methods to dijit global
], function(declare, domClass, has, keys, lang, on, focus, ContentPane, _DialogMixin, _FormMixin, _TemplatedMixin, template, dijit){
// module:
// dijit/TooltipDialog
var TooltipDialog = declare("dijit.TooltipDialog",
[ContentPane, _TemplatedMixin, _FormMixin, _DialogMixin], {
// summary:
// Pops up a dialog that appears like a Tooltip
// title: String
// Description of tooltip dialog (required for a11y)
title: "",
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
// is never a child of a layout container, nor can you specify the size of
// TooltipDialog in order to control the size of an inner widget.
doLayout: false,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true.
autofocus: true,
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: "dijitTooltipDialog",
// _firstFocusItem: [private readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_firstFocusItem: null,
// _lastFocusItem: [private readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_lastFocusItem: null,
templateString: template,
_setTitleAttr: "containerNode",
postCreate: function(){
this.inherited(arguments);
this.own(on(this.domNode, "keydown", lang.hitch(this, "_onKey")));
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner){
// summary:
// Configure widget to be displayed in given position relative to the button.
// This is called from the dijit.popup code, and should not be called
// directly.
// tags:
// protected
// Note: intentionally not using dijitTooltip class since that sets position:absolute, which
// confuses dijit/popup trying to get the size of the tooltip.
var newC = {
// Real around node
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft",
// Positioning "around" a point, ex: mouse position
"BR-TL": "dijitTooltipBelow dijitTooltipABLeft",
"BL-TR": "dijitTooltipBelow dijitTooltipABRight",
"TL-BR": "dijitTooltipAbove dijitTooltipABRight",
"TR-BL": "dijitTooltipAbove dijitTooltipABLeft"
}[aroundCorner + "-" + tooltipCorner];
domClass.replace(this.domNode, newC, this._currentOrientClass || "");
this._currentOrientClass = newC;
// Tooltip.orient() has code to reposition connector for when Tooltip is before/after anchor.
// Not putting here to avoid code bloat, and since TooltipDialogs are generally above/below.
// Should combine code from Tooltip and TooltipDialog.
},
focus: function(){
// summary:
// Focus on first field
this._getFocusItems();
focus.focus(this._firstFocusItem);
},
onOpen: function(/*Object*/ pos){
// summary:
// Called when dialog is displayed.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.orient(this.domNode, pos.aroundCorner, pos.corner);
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}
this._onShow(); // lazy load trigger (TODO: shouldn't we load before positioning?)
},
onClose: function(){
// summary:
// Called when dialog is hidden.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.onHide();
},
_onKey: function(/*Event*/ evt){
// summary:
// Handler for keydown events
// description:
// Keep keyboard focus in dialog; close dialog on escape key
// tags:
// private
if(evt.keyCode == keys.ESCAPE){
// Use defer to avoid crash on IE, see #10396. Not sure if this is still needed or not.
// If this if() wasn't here, presumably dijit/popup would catch the ESCAPE key and close the popup.
this.defer("onCancel");
evt.stopPropagation();
evt.preventDefault();
}else if(evt.keyCode == keys.TAB){
var node = evt.target;
this._getFocusItems();
if(this._firstFocusItem == this._lastFocusItem){
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._firstFocusItem && evt.shiftKey){
focus.focus(this._lastFocusItem); // send focus to last item in dialog
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._lastFocusItem && !evt.shiftKey){
focus.focus(this._firstFocusItem); // send focus to first item in dialog
evt.stopPropagation();
evt.preventDefault();
}else{
// we want the browser's default tab handling to move focus
// but we don't want the tab to propagate upwards
evt.stopPropagation();
}
}
}
});
if(has("dojo-bidi")){
TooltipDialog.extend({
_setTitleAttr: function(/*String*/ title){
this.containerNode.title = (this.textDir && this.enforceTextDirWithUcc) ? this.enforceTextDirWithUcc(null, title) : title;
this._set("title", title);
},
_setTextDirAttr: function(/*String*/ textDir){
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
if(this.textDir && this.title){
this.containerNode.title = this.enforceTextDirWithUcc(null, this.title);
}
}
}
});
}
return TooltipDialog;
});
@@ -1,200 +0,0 @@
require({cache:{
'url:dijit/templates/TooltipDialog.html':"<div role=\"alertdialog\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"contentsNode\" class=\"dijitTooltipContents dijitTooltipFocusNode\">\n\t\t\t<div data-dojo-attach-point=\"containerNode\"></div>\n\t\t\t${!actionBarTemplate}\n\t\t</div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n"}});
define("dijit/TooltipDialog", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.replace
"dojo/has",
"dojo/keys", // keys
"dojo/_base/lang", // lang.hitch
"dojo/on",
"./focus",
"./layout/ContentPane",
"./_DialogMixin",
"./form/_FormMixin",
"./_TemplatedMixin",
"dojo/text!./templates/TooltipDialog.html",
"./main" // exports methods to dijit global
], function(declare, domClass, has, keys, lang, on, focus, ContentPane, _DialogMixin, _FormMixin, _TemplatedMixin, template, dijit){
// module:
// dijit/TooltipDialog
var TooltipDialog = declare("dijit.TooltipDialog",
[ContentPane, _TemplatedMixin, _FormMixin, _DialogMixin], {
// summary:
// Pops up a dialog that appears like a Tooltip
// title: String
// Description of tooltip dialog (required for a11y)
title: "",
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
// is never a child of a layout container, nor can you specify the size of
// TooltipDialog in order to control the size of an inner widget.
doLayout: false,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true.
autofocus: true,
// baseClass: [protected] String
// The root className to use for the various states of this widget
baseClass: "dijitTooltipDialog",
// _firstFocusItem: [private readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_firstFocusItem: null,
// _lastFocusItem: [private readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_lastFocusItem: null,
templateString: template,
_setTitleAttr: "containerNode",
postCreate: function(){
this.inherited(arguments);
this.own(on(this.domNode, "keydown", lang.hitch(this, "_onKey")));
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner){
// summary:
// Configure widget to be displayed in given position relative to the button.
// This is called from the dijit.popup code, and should not be called
// directly.
// tags:
// protected
// Note: intentionally not using dijitTooltip class since that sets position:absolute, which
// confuses dijit/popup trying to get the size of the tooltip.
var newC = {
// Real around node
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft",
// Positioning "around" a point, ex: mouse position
"BR-TL": "dijitTooltipBelow dijitTooltipABLeft",
"BL-TR": "dijitTooltipBelow dijitTooltipABRight",
"TL-BR": "dijitTooltipAbove dijitTooltipABRight",
"TR-BL": "dijitTooltipAbove dijitTooltipABLeft"
}[aroundCorner + "-" + tooltipCorner];
domClass.replace(this.domNode, newC, this._currentOrientClass || "");
this._currentOrientClass = newC;
// Tooltip.orient() has code to reposition connector for when Tooltip is before/after anchor.
// Not putting here to avoid code bloat, and since TooltipDialogs are generally above/below.
// Should combine code from Tooltip and TooltipDialog.
},
focus: function(){
// summary:
// Focus on first field
this._getFocusItems();
focus.focus(this._firstFocusItem);
},
onOpen: function(/*Object*/ pos){
// summary:
// Called when dialog is displayed.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.orient(this.domNode, pos.aroundCorner, pos.corner);
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}
this._onShow(); // lazy load trigger (TODO: shouldn't we load before positioning?)
},
onClose: function(){
// summary:
// Called when dialog is hidden.
// This is called from the dijit.popup code, and should not be called directly.
// tags:
// protected
this.onHide();
},
_onKey: function(/*Event*/ evt){
// summary:
// Handler for keydown events
// description:
// Keep keyboard focus in dialog; close dialog on escape key
// tags:
// private
if(evt.keyCode == keys.ESCAPE){
// Use defer to avoid crash on IE, see #10396. Not sure if this is still needed or not.
// If this if() wasn't here, presumably dijit/popup would catch the ESCAPE key and close the popup.
this.defer("onCancel");
evt.stopPropagation();
evt.preventDefault();
}else if(evt.keyCode == keys.TAB){
var node = evt.target;
this._getFocusItems();
if(this._firstFocusItem == this._lastFocusItem){
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._firstFocusItem && evt.shiftKey){
focus.focus(this._lastFocusItem); // send focus to last item in dialog
evt.stopPropagation();
evt.preventDefault();
}else if(node == this._lastFocusItem && !evt.shiftKey){
focus.focus(this._firstFocusItem); // send focus to first item in dialog
evt.stopPropagation();
evt.preventDefault();
}else{
// we want the browser's default tab handling to move focus
// but we don't want the tab to propagate upwards
evt.stopPropagation();
}
}
}
});
if(has("dojo-bidi")){
TooltipDialog.extend({
_setTitleAttr: function(/*String*/ title){
this.containerNode.title = (this.textDir && this.enforceTextDirWithUcc) ? this.enforceTextDirWithUcc(null, title) : title;
this._set("title", title);
},
_setTextDirAttr: function(/*String*/ textDir){
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
if(this.textDir && this.title){
this.containerNode.title = this.enforceTextDirWithUcc(null, this.title);
}
}
}
});
}
return TooltipDialog;
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,44 +1,87 @@
//>>built
define("dijit/Viewport",["dojo/Evented","dojo/on","dojo/domReady","dojo/sniff","dojo/window"],function(_1,on,_2,_3,_4){
var _5=new _1();
var _6;
_2(function(){
var _7=_4.getBox();
_5._rlh=on(window,"resize",function(){
var _8=_4.getBox();
if(_7.h==_8.h&&_7.w==_8.w){
return;
}
_7=_8;
_5.emit("resize");
});
if(_3("ie")==8){
var _9=screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI!=_9){
_9=screen.deviceXDPI;
_5.emit("resize");
}
},500);
}
if(_3("ios")){
on(document,"focusin",function(_a){
_6=_a.target;
});
on(document,"focusout",function(_b){
_6=null;
});
}
});
_5.getEffectiveBox=function(_c){
var _d=_4.getBox(_c);
var _e=_6&&_6.tagName&&_6.tagName.toLowerCase();
if(_3("ios")&&_6&&!_6.readOnly&&(_e=="textarea"||(_e=="input"&&/^(color|email|number|password|search|tel|text|url)$/.test(_6.type)))){
_d.h*=(orientation==0||orientation==180?0.66:0.4);
var _f=_6.getBoundingClientRect();
_d.h=Math.max(_d.h,_f.top+_f.height);
}
return _d;
};
return _5;
define([
"dojo/Evented",
"dojo/on",
"dojo/domReady",
"dojo/sniff", // has("ie"), has("ios")
"dojo/window" // getBox()
], function(Evented, on, domReady, has, winUtils){
// module:
// dijit/Viewport
/*=====
return {
// summary:
// Utility singleton to watch for viewport resizes, avoiding duplicate notifications
// which can lead to infinite loops.
// description:
// Usage: Viewport.on("resize", myCallback).
//
// myCallback() is called without arguments in case it's _WidgetBase.resize(),
// which would interpret the argument as the size to make the widget.
};
=====*/
var Viewport = new Evented();
var focusedNode;
domReady(function(){
var oldBox = winUtils.getBox();
Viewport._rlh = on(window, "resize", function(){
var newBox = winUtils.getBox();
if(oldBox.h == newBox.h && oldBox.w == newBox.w){ return; }
oldBox = newBox;
Viewport.emit("resize");
});
// Also catch zoom changes on IE8, since they don't naturally generate resize events
if(has("ie") == 8){
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
Viewport.emit("resize");
}
}, 500);
}
// On iOS, keep track of the focused node so we can guess when the keyboard is/isn't being displayed.
if(has("ios")){
on(document, "focusin", function(evt){
focusedNode = evt.target;
});
on(document, "focusout", function(evt){
focusedNode = null;
});
}
});
Viewport.getEffectiveBox = function(/*Document*/ doc){
// summary:
// Get the size of the viewport, or on mobile devices, the part of the viewport not obscured by the
// virtual keyboard.
var box = winUtils.getBox(doc);
// Account for iOS virtual keyboard, if it's being shown. Unfortunately no direct way to check or measure.
var tag = focusedNode && focusedNode.tagName && focusedNode.tagName.toLowerCase();
if(has("ios") && focusedNode && !focusedNode.readOnly && (tag == "textarea" || (tag == "input" &&
/^(color|email|number|password|search|tel|text|url)$/.test(focusedNode.type)))){
// Box represents the size of the viewport. Some of the viewport is likely covered by the keyboard.
// Estimate height of visible viewport assuming viewport goes to bottom of screen, but is covered by keyboard.
box.h *= (orientation == 0 || orientation == 180 ? 0.66 : 0.40);
// Above measurement will be inaccurate if viewport was scrolled up so far that it ends before the bottom
// of the screen. In this case, keyboard isn't covering as much of the viewport as we thought.
// We know the visible size is at least the distance from the top of the viewport to the focused node.
var rect = focusedNode.getBoundingClientRect();
box.h = Math.max(box.h, rect.top + rect.height);
}
return box;
};
return Viewport;
});
@@ -1,87 +0,0 @@
define("dijit/Viewport", [
"dojo/Evented",
"dojo/on",
"dojo/domReady",
"dojo/sniff", // has("ie"), has("ios")
"dojo/window" // getBox()
], function(Evented, on, domReady, has, winUtils){
// module:
// dijit/Viewport
/*=====
return {
// summary:
// Utility singleton to watch for viewport resizes, avoiding duplicate notifications
// which can lead to infinite loops.
// description:
// Usage: Viewport.on("resize", myCallback).
//
// myCallback() is called without arguments in case it's _WidgetBase.resize(),
// which would interpret the argument as the size to make the widget.
};
=====*/
var Viewport = new Evented();
var focusedNode;
domReady(function(){
var oldBox = winUtils.getBox();
Viewport._rlh = on(window, "resize", function(){
var newBox = winUtils.getBox();
if(oldBox.h == newBox.h && oldBox.w == newBox.w){ return; }
oldBox = newBox;
Viewport.emit("resize");
});
// Also catch zoom changes on IE8, since they don't naturally generate resize events
if(has("ie") == 8){
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
Viewport.emit("resize");
}
}, 500);
}
// On iOS, keep track of the focused node so we can guess when the keyboard is/isn't being displayed.
if(has("ios")){
on(document, "focusin", function(evt){
focusedNode = evt.target;
});
on(document, "focusout", function(evt){
focusedNode = null;
});
}
});
Viewport.getEffectiveBox = function(/*Document*/ doc){
// summary:
// Get the size of the viewport, or on mobile devices, the part of the viewport not obscured by the
// virtual keyboard.
var box = winUtils.getBox(doc);
// Account for iOS virtual keyboard, if it's being shown. Unfortunately no direct way to check or measure.
var tag = focusedNode && focusedNode.tagName && focusedNode.tagName.toLowerCase();
if(has("ios") && focusedNode && !focusedNode.readOnly && (tag == "textarea" || (tag == "input" &&
/^(color|email|number|password|search|tel|text|url)$/.test(focusedNode.type)))){
// Box represents the size of the viewport. Some of the viewport is likely covered by the keyboard.
// Estimate height of visible viewport assuming viewport goes to bottom of screen, but is covered by keyboard.
box.h *= (orientation == 0 || orientation == 180 ? 0.66 : 0.40);
// Above measurement will be inaccurate if viewport was scrolled up so far that it ends before the bottom
// of the screen. In this case, keyboard isn't covering as much of the viewport as we thought.
// We know the visible size is at least the distance from the top of the viewport to the focused node.
var rect = focusedNode.getBoundingClientRect();
box.h = Math.max(box.h, rect.top + rect.height);
}
return box;
};
return Viewport;
});
@@ -1,76 +1,247 @@
//>>built
define("dijit/WidgetSet",["dojo/_base/array","dojo/_base/declare","dojo/_base/kernel","./registry"],function(_1,_2,_3,_4){
var _5=_2("dijit.WidgetSet",null,{constructor:function(){
this._hash={};
this.length=0;
},add:function(_6){
if(this._hash[_6.id]){
throw new Error("Tried to register widget with id=="+_6.id+" but that id is already registered");
}
this._hash[_6.id]=_6;
this.length++;
},remove:function(id){
if(this._hash[id]){
delete this._hash[id];
this.length--;
}
},forEach:function(_7,_8){
_8=_8||_3.global;
var i=0,id;
for(id in this._hash){
_7.call(_8,this._hash[id],i++,this._hash);
}
return this;
},filter:function(_9,_a){
_a=_a||_3.global;
var _b=new _5(),i=0,id;
for(id in this._hash){
var w=this._hash[id];
if(_9.call(_a,w,i++,this._hash)){
_b.add(w);
}
}
return _b;
},byId:function(id){
return this._hash[id];
},byClass:function(_c){
var _d=new _5(),id,_e;
for(id in this._hash){
_e=this._hash[id];
if(_e.declaredClass==_c){
_d.add(_e);
}
}
return _d;
},toArray:function(){
var ar=[];
for(var id in this._hash){
ar.push(this._hash[id]);
}
return ar;
},map:function(_f,_10){
return _1.map(this.toArray(),_f,_10);
},every:function(_11,_12){
_12=_12||_3.global;
var x=0,i;
for(i in this._hash){
if(!_11.call(_12,this._hash[i],x++,this._hash)){
return false;
}
}
return true;
},some:function(_13,_14){
_14=_14||_3.global;
var x=0,i;
for(i in this._hash){
if(_13.call(_14,this._hash[i],x++,this._hash)){
return true;
}
}
return false;
}});
_1.forEach(["forEach","filter","byClass","map","every","some"],function(_15){
_4[_15]=_5.prototype[_15];
});
return _5;
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.global
"./registry" // to add functions to dijit.registry
], function(array, declare, kernel, registry){
// module:
// dijit/WidgetSet
var WidgetSet = declare("dijit.WidgetSet", null, {
// summary:
// A set of widgets indexed by id.
// Deprecated, will be removed in 2.0.
//
// example:
// Create a small list of widgets:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("one"));
// | ws.add(registry.byId("two"));
// | // destroy both:
// | ws.forEach(function(w){ w.destroy(); });
// | });
constructor: function(){
this._hash = {};
this.length = 0;
},
add: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Add a widget to this list. If a duplicate ID is detected, a error is thrown.
//
// widget: dijit/_WidgetBase
// Any dijit/_WidgetBase subclass.
if(this._hash[widget.id]){
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
}
this._hash[widget.id] = widget;
this.length++;
},
remove: function(/*String*/ id){
// summary:
// Remove a widget from this WidgetSet. Does not destroy the widget; simply
// removes the reference.
if(this._hash[id]){
delete this._hash[id];
this.length--;
}
},
forEach: function(/*Function*/ func, /* Object? */thisObj){
// summary:
// Call specified function for each widget in this set.
//
// func:
// A callback function to run for each item. Is passed the widget, the index
// in the iteration, and the full hash, similar to `array.forEach`.
//
// thisObj:
// An optional scope parameter
//
// example:
// Using the default `dijit.registry` instance:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.forEach(function(widget){
// | console.log(widget.declaredClass);
// | });
// | });
//
// returns:
// Returns self, in order to allow for further chaining.
thisObj = thisObj || kernel.global;
var i = 0, id;
for(id in this._hash){
func.call(thisObj, this._hash[id], i++, this._hash);
}
return this; // dijit/WidgetSet
},
filter: function(/*Function*/ filter, /* Object? */thisObj){
// summary:
// Filter down this WidgetSet to a smaller new WidgetSet
// Works the same as `array.filter` and `NodeList.filter`
//
// filter:
// Callback function to test truthiness. Is passed the widget
// reference and the pseudo-index in the object.
//
// thisObj: Object?
// Option scope to use for the filter function.
//
// example:
// Arbitrary: select the odd widgets in this list
// |
// |
// |
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.filter(function(w, i){
// | return i % 2 == 0;
// | }).forEach(function(w){ /* odd ones */ });
// | });
thisObj = thisObj || kernel.global;
var res = new WidgetSet(), i = 0, id;
for(id in this._hash){
var w = this._hash[id];
if(filter.call(thisObj, w, i++, this._hash)){
res.add(w);
}
}
return res; // dijit/WidgetSet
},
byId: function(/*String*/ id){
// summary:
// Find a widget in this list by it's id.
// example:
// Test if an id is in a particular WidgetSet
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("bar"));
// | var t = ws.byId("bar") // returns a widget
// | var x = ws.byId("foo"); // returns undefined
// | });
return this._hash[id]; // dijit/_WidgetBase
},
byClass: function(/*String*/ cls){
// summary:
// Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
//
// cls: String
// The Class to scan for. Full dot-notated string.
//
// example:
// Find all `dijit.TitlePane`s in a page:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
// | });
var res = new WidgetSet(), id, widget;
for(id in this._hash){
widget = this._hash[id];
if(widget.declaredClass == cls){
res.add(widget);
}
}
return res; // dijit/WidgetSet
},
toArray: function(){
// summary:
// Convert this WidgetSet into a true Array
//
// example:
// Work with the widget .domNodes in a real Array
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | array.map(registry.toArray(), function(w){ return w.domNode; });
// | });
var ar = [];
for(var id in this._hash){
ar.push(this._hash[id]);
}
return ar; // dijit/_WidgetBase[]
},
map: function(/* Function */func, /* Object? */thisObj){
// summary:
// Create a new Array from this WidgetSet, following the same rules as `array.map`
// example:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var nodes = registry.map(function(w){ return w.domNode; });
// | });
//
// returns:
// A new array of the returned values.
return array.map(this.toArray(), func, thisObj); // Array
},
every: function(func, thisObj){
// summary:
// A synthetic clone of `array.every` acting explicitly on this WidgetSet
//
// func: Function
// A callback function run for every widget in this list. Exits loop
// when the first false return is encountered.
//
// thisObj: Object?
// Optional scope parameter to use for the callback
thisObj = thisObj || kernel.global;
var x = 0, i;
for(i in this._hash){
if(!func.call(thisObj, this._hash[i], x++, this._hash)){
return false; // Boolean
}
}
return true; // Boolean
},
some: function(func, thisObj){
// summary:
// A synthetic clone of `array.some` acting explicitly on this WidgetSet
//
// func: Function
// A callback function run for every widget in this list. Exits loop
// when the first true return is encountered.
//
// thisObj: Object?
// Optional scope parameter to use for the callback
thisObj = thisObj || kernel.global;
var x = 0, i;
for(i in this._hash){
if(func.call(thisObj, this._hash[i], x++, this._hash)){
return true; // Boolean
}
}
return false; // Boolean
}
});
// Add in 1.x compatibility methods to dijit/registry.
// These functions won't show up in the API doc but since they are deprecated anyway,
// that's probably for the best.
array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
registry[func] = WidgetSet.prototype[func];
});
return WidgetSet;
});
@@ -1,247 +0,0 @@
define("dijit/WidgetSet", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.global
"./registry" // to add functions to dijit.registry
], function(array, declare, kernel, registry){
// module:
// dijit/WidgetSet
var WidgetSet = declare("dijit.WidgetSet", null, {
// summary:
// A set of widgets indexed by id.
// Deprecated, will be removed in 2.0.
//
// example:
// Create a small list of widgets:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("one"));
// | ws.add(registry.byId("two"));
// | // destroy both:
// | ws.forEach(function(w){ w.destroy(); });
// | });
constructor: function(){
this._hash = {};
this.length = 0;
},
add: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Add a widget to this list. If a duplicate ID is detected, a error is thrown.
//
// widget: dijit/_WidgetBase
// Any dijit/_WidgetBase subclass.
if(this._hash[widget.id]){
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
}
this._hash[widget.id] = widget;
this.length++;
},
remove: function(/*String*/ id){
// summary:
// Remove a widget from this WidgetSet. Does not destroy the widget; simply
// removes the reference.
if(this._hash[id]){
delete this._hash[id];
this.length--;
}
},
forEach: function(/*Function*/ func, /* Object? */thisObj){
// summary:
// Call specified function for each widget in this set.
//
// func:
// A callback function to run for each item. Is passed the widget, the index
// in the iteration, and the full hash, similar to `array.forEach`.
//
// thisObj:
// An optional scope parameter
//
// example:
// Using the default `dijit.registry` instance:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.forEach(function(widget){
// | console.log(widget.declaredClass);
// | });
// | });
//
// returns:
// Returns self, in order to allow for further chaining.
thisObj = thisObj || kernel.global;
var i = 0, id;
for(id in this._hash){
func.call(thisObj, this._hash[id], i++, this._hash);
}
return this; // dijit/WidgetSet
},
filter: function(/*Function*/ filter, /* Object? */thisObj){
// summary:
// Filter down this WidgetSet to a smaller new WidgetSet
// Works the same as `array.filter` and `NodeList.filter`
//
// filter:
// Callback function to test truthiness. Is passed the widget
// reference and the pseudo-index in the object.
//
// thisObj: Object?
// Option scope to use for the filter function.
//
// example:
// Arbitrary: select the odd widgets in this list
// |
// |
// |
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.filter(function(w, i){
// | return i % 2 == 0;
// | }).forEach(function(w){ /* odd ones */ });
// | });
thisObj = thisObj || kernel.global;
var res = new WidgetSet(), i = 0, id;
for(id in this._hash){
var w = this._hash[id];
if(filter.call(thisObj, w, i++, this._hash)){
res.add(w);
}
}
return res; // dijit/WidgetSet
},
byId: function(/*String*/ id){
// summary:
// Find a widget in this list by it's id.
// example:
// Test if an id is in a particular WidgetSet
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("bar"));
// | var t = ws.byId("bar") // returns a widget
// | var x = ws.byId("foo"); // returns undefined
// | });
return this._hash[id]; // dijit/_WidgetBase
},
byClass: function(/*String*/ cls){
// summary:
// Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
//
// cls: String
// The Class to scan for. Full dot-notated string.
//
// example:
// Find all `dijit.TitlePane`s in a page:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
// | });
var res = new WidgetSet(), id, widget;
for(id in this._hash){
widget = this._hash[id];
if(widget.declaredClass == cls){
res.add(widget);
}
}
return res; // dijit/WidgetSet
},
toArray: function(){
// summary:
// Convert this WidgetSet into a true Array
//
// example:
// Work with the widget .domNodes in a real Array
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | array.map(registry.toArray(), function(w){ return w.domNode; });
// | });
var ar = [];
for(var id in this._hash){
ar.push(this._hash[id]);
}
return ar; // dijit/_WidgetBase[]
},
map: function(/* Function */func, /* Object? */thisObj){
// summary:
// Create a new Array from this WidgetSet, following the same rules as `array.map`
// example:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var nodes = registry.map(function(w){ return w.domNode; });
// | });
//
// returns:
// A new array of the returned values.
return array.map(this.toArray(), func, thisObj); // Array
},
every: function(func, thisObj){
// summary:
// A synthetic clone of `array.every` acting explicitly on this WidgetSet
//
// func: Function
// A callback function run for every widget in this list. Exits loop
// when the first false return is encountered.
//
// thisObj: Object?
// Optional scope parameter to use for the callback
thisObj = thisObj || kernel.global;
var x = 0, i;
for(i in this._hash){
if(!func.call(thisObj, this._hash[i], x++, this._hash)){
return false; // Boolean
}
}
return true; // Boolean
},
some: function(func, thisObj){
// summary:
// A synthetic clone of `array.some` acting explicitly on this WidgetSet
//
// func: Function
// A callback function run for every widget in this list. Exits loop
// when the first true return is encountered.
//
// thisObj: Object?
// Optional scope parameter to use for the callback
thisObj = thisObj || kernel.global;
var x = 0, i;
for(i in this._hash){
if(func.call(thisObj, this._hash[i], x++, this._hash)){
return true; // Boolean
}
}
return false; // Boolean
}
});
// Add in 1.x compatibility methods to dijit/registry.
// These functions won't show up in the API doc but since they are deprecated anyway,
// that's probably for the best.
array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
registry[func] = WidgetSet.prototype[func];
});
return WidgetSet;
});
@@ -1,94 +1,238 @@
//>>built
define("dijit/_AttachMixin",["require","dojo/_base/array","dojo/_base/connect","dojo/_base/declare","dojo/_base/lang","dojo/mouse","dojo/on","dojo/touch","./_WidgetBase"],function(_1,_2,_3,_4,_5,_6,on,_7,_8){
var _9=_5.delegate(_7,{"mouseenter":_6.enter,"mouseleave":_6.leave,"keypress":_3._keypress});
var _a;
var _b=_4("dijit._AttachMixin",null,{constructor:function(){
this._attachPoints=[];
this._attachEvents=[];
},buildRendering:function(){
this.inherited(arguments);
this._attachTemplateNodes(this.domNode);
this._beforeFillContent();
},_beforeFillContent:function(){
},_attachTemplateNodes:function(_c){
var _d=_c;
while(true){
if(_d.nodeType==1&&(this._processTemplateNode(_d,function(n,p){
return n.getAttribute(p);
},this._attach)||this.searchContainerNode)&&_d.firstChild){
_d=_d.firstChild;
}else{
if(_d==_c){
return;
}
while(!_d.nextSibling){
_d=_d.parentNode;
if(_d==_c){
return;
}
}
_d=_d.nextSibling;
}
}
},_processTemplateNode:function(_e,_f,_10){
var ret=true;
var _11=this.attachScope||this,_12=_f(_e,"dojoAttachPoint")||_f(_e,"data-dojo-attach-point");
if(_12){
var _13,_14=_12.split(/\s*,\s*/);
while((_13=_14.shift())){
if(_5.isArray(_11[_13])){
_11[_13].push(_e);
}else{
_11[_13]=_e;
}
ret=(_13!="containerNode");
this._attachPoints.push(_13);
}
}
var _15=_f(_e,"dojoAttachEvent")||_f(_e,"data-dojo-attach-event");
if(_15){
var _16,_17=_15.split(/\s*,\s*/);
var _18=_5.trim;
while((_16=_17.shift())){
if(_16){
var _19=null;
if(_16.indexOf(":")!=-1){
var _1a=_16.split(":");
_16=_18(_1a[0]);
_19=_18(_1a[1]);
}else{
_16=_18(_16);
}
if(!_19){
_19=_16;
}
this._attachEvents.push(_10(_e,_16,_5.hitch(_11,_19)));
}
}
}
return ret;
},_attach:function(_1b,_1c,_1d){
_1c=_1c.replace(/^on/,"").toLowerCase();
if(_1c=="dijitclick"){
_1c=_a||(_a=_1("./a11yclick"));
}else{
_1c=_9[_1c]||_1c;
}
return on(_1b,_1c,_1d);
},_detachTemplateNodes:function(){
var _1e=this.attachScope||this;
_2.forEach(this._attachPoints,function(_1f){
delete _1e[_1f];
});
this._attachPoints=[];
_2.forEach(this._attachEvents,function(_20){
_20.remove();
});
this._attachEvents=[];
},destroyRendering:function(){
this._detachTemplateNodes();
this.inherited(arguments);
}});
_5.extend(_8,{dojoAttachEvent:"",dojoAttachPoint:""});
return _b;
define([
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/connect", // remove for 2.0
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"dojo/mouse",
"dojo/on",
"dojo/touch",
"./_WidgetBase"
], function(require, array, connect, declare, lang, mouse, on, touch, _WidgetBase){
// module:
// dijit/_AttachMixin
// Map from string name like "mouseenter" to synthetic event like mouse.enter
var synthEvents = lang.delegate(touch, {
"mouseenter": mouse.enter,
"mouseleave": mouse.leave,
"keypress": connect._keypress // remove for 2.0
});
// To be lightweight, _AttachMixin doesn't require() dijit/a11yclick.
// If the subclass has a template using "ondijitclick", it must load dijit/a11yclick itself.
// In that case, the a11yclick variable below will get set to point to that synthetic event.
var a11yclick;
var _AttachMixin = declare("dijit._AttachMixin", null, {
// summary:
// Mixin for widgets to attach to dom nodes and setup events via
// convenient data-dojo-attach-point and data-dojo-attach-event DOM attributes.
//
// Superclass of _TemplatedMixin, and can also be used standalone when templates are pre-rendered on the
// server.
//
// Does not [yet] handle widgets like ContentPane with this.containerNode set. It should skip
// scanning for data-dojo-attach-point and data-dojo-attach-event inside this.containerNode, but it
// doesn't.
/*=====
// _attachPoints: [private] String[]
// List of widget attribute names associated with data-dojo-attach-point=... in the
// template, ex: ["containerNode", "labelNode"]
_attachPoints: [],
// _attachEvents: [private] Handle[]
// List of connections associated with data-dojo-attach-event=... in the
// template
_attachEvents: [],
// attachScope: [public] Object
// Object to which attach points and events will be scoped. Defaults
// to 'this'.
attachScope: undefined,
// searchContainerNode: [protected] Boolean
// Search descendants of this.containerNode for data-dojo-attach-point and data-dojo-attach-event.
// Should generally be left false (the default value) both for performance and to avoid failures when
// this.containerNode holds other _AttachMixin instances with their own attach points and events.
searchContainerNode: false,
=====*/
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
this._attachPoints = [];
this._attachEvents = [];
},
buildRendering: function(){
// summary:
// Attach to DOM nodes marked with special attributes.
// tags:
// protected
this.inherited(arguments);
// recurse through the node, looking for, and attaching to, our
// attachment points and events, which should be defined on the template node.
this._attachTemplateNodes(this.domNode);
this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
},
_beforeFillContent: function(){
},
_attachTemplateNodes: function(rootNode){
// summary:
// Iterate through the dom nodes and attach functions and nodes accordingly.
// description:
// Map widget properties and functions to the handlers specified in
// the dom node and it's descendants. This function iterates over all
// nodes and looks for these properties:
//
// - dojoAttachPoint/data-dojo-attach-point
// - dojoAttachEvent/data-dojo-attach-event
// rootNode: DomNode
// The node to search for properties. All descendants will be searched.
// tags:
// private
// DFS to process all nodes except those inside of this.containerNode
var node = rootNode;
while(true){
if(node.nodeType == 1 && (this._processTemplateNode(node, function(n,p){ return n.getAttribute(p); },
this._attach) || this.searchContainerNode) && node.firstChild){
node = node.firstChild;
}else{
if(node == rootNode){ return; }
while(!node.nextSibling){
node = node.parentNode;
if(node == rootNode){ return; }
}
node = node.nextSibling;
}
}
},
_processTemplateNode: function(/*DOMNode|Widget*/ baseNode, getAttrFunc, attachFunc){
// summary:
// Process data-dojo-attach-point and data-dojo-attach-event for given node or widget.
// Returns true if caller should process baseNode's children too.
var ret = true;
// Process data-dojo-attach-point
var _attachScope = this.attachScope || this,
attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
if(attachPoint){
var point, points = attachPoint.split(/\s*,\s*/);
while((point = points.shift())){
if(lang.isArray(_attachScope[point])){
_attachScope[point].push(baseNode);
}else{
_attachScope[point] = baseNode;
}
ret = (point != "containerNode");
this._attachPoints.push(point);
}
}
// Process data-dojo-attach-event
var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
if(attachEvent){
// NOTE: we want to support attributes that have the form
// "domEvent: nativeEvent; ..."
var event, events = attachEvent.split(/\s*,\s*/);
var trim = lang.trim;
while((event = events.shift())){
if(event){
var thisFunc = null;
if(event.indexOf(":") != -1){
// oh, if only JS had tuple assignment
var funcNameArr = event.split(":");
event = trim(funcNameArr[0]);
thisFunc = trim(funcNameArr[1]);
}else{
event = trim(event);
}
if(!thisFunc){
thisFunc = event;
}
this._attachEvents.push(attachFunc(baseNode, event, lang.hitch(_attachScope, thisFunc)));
}
}
}
return ret;
},
_attach: function(node, type, func){
// summary:
// Roughly corresponding to dojo/on, this is the default function for processing a
// data-dojo-attach-event. Meant to attach to DOMNodes, not to widgets.
// node: DOMNode
// The node to setup a listener on.
// type: String
// Event name like "click".
// getAttrFunc: Function
// Function to get the specified property for a given DomNode/Widget.
// attachFunc: Function?
// Attaches an event handler from the specified node/widget to specified function.
// Map special type names like "mouseenter" to synthetic events.
// Subclasses are responsible to require() dijit/a11yclick if they want to use it.
type = type.replace(/^on/, "").toLowerCase();
if(type == "dijitclick"){
type = a11yclick || (a11yclick = require("./a11yclick"));
}else{
type = synthEvents[type] || type;
}
return on(node, type, func);
},
_detachTemplateNodes: function() {
// summary:
// Detach and clean up the attachments made in _attachtempalteNodes.
// Delete all attach points to prevent IE6 memory leaks.
var _attachScope = this.attachScope || this;
array.forEach(this._attachPoints, function(point){
delete _attachScope[point];
});
this._attachPoints = [];
// And same for event handlers
array.forEach(this._attachEvents, function(handle){ handle.remove(); });
this._attachEvents = [];
},
destroyRendering: function(){
this._detachTemplateNodes();
this.inherited(arguments);
}
});
// These arguments can be specified for widgets which are used in templates.
// Since any widget can be specified as sub widgets in template, mix it
// into the base widget class. (This is a hack, but it's effective.).
// Remove for 2.0. Also, hide from API doc parser.
lang.extend(_WidgetBase, /*===== {} || =====*/ {
dojoAttachEvent: "",
dojoAttachPoint: ""
});
return _AttachMixin;
});
@@ -1,238 +0,0 @@
define("dijit/_AttachMixin", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/connect", // remove for 2.0
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.getObject
"dojo/mouse",
"dojo/on",
"dojo/touch",
"./_WidgetBase"
], function(require, array, connect, declare, lang, mouse, on, touch, _WidgetBase){
// module:
// dijit/_AttachMixin
// Map from string name like "mouseenter" to synthetic event like mouse.enter
var synthEvents = lang.delegate(touch, {
"mouseenter": mouse.enter,
"mouseleave": mouse.leave,
"keypress": connect._keypress // remove for 2.0
});
// To be lightweight, _AttachMixin doesn't require() dijit/a11yclick.
// If the subclass has a template using "ondijitclick", it must load dijit/a11yclick itself.
// In that case, the a11yclick variable below will get set to point to that synthetic event.
var a11yclick;
var _AttachMixin = declare("dijit._AttachMixin", null, {
// summary:
// Mixin for widgets to attach to dom nodes and setup events via
// convenient data-dojo-attach-point and data-dojo-attach-event DOM attributes.
//
// Superclass of _TemplatedMixin, and can also be used standalone when templates are pre-rendered on the
// server.
//
// Does not [yet] handle widgets like ContentPane with this.containerNode set. It should skip
// scanning for data-dojo-attach-point and data-dojo-attach-event inside this.containerNode, but it
// doesn't.
/*=====
// _attachPoints: [private] String[]
// List of widget attribute names associated with data-dojo-attach-point=... in the
// template, ex: ["containerNode", "labelNode"]
_attachPoints: [],
// _attachEvents: [private] Handle[]
// List of connections associated with data-dojo-attach-event=... in the
// template
_attachEvents: [],
// attachScope: [public] Object
// Object to which attach points and events will be scoped. Defaults
// to 'this'.
attachScope: undefined,
// searchContainerNode: [protected] Boolean
// Search descendants of this.containerNode for data-dojo-attach-point and data-dojo-attach-event.
// Should generally be left false (the default value) both for performance and to avoid failures when
// this.containerNode holds other _AttachMixin instances with their own attach points and events.
searchContainerNode: false,
=====*/
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
this._attachPoints = [];
this._attachEvents = [];
},
buildRendering: function(){
// summary:
// Attach to DOM nodes marked with special attributes.
// tags:
// protected
this.inherited(arguments);
// recurse through the node, looking for, and attaching to, our
// attachment points and events, which should be defined on the template node.
this._attachTemplateNodes(this.domNode);
this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
},
_beforeFillContent: function(){
},
_attachTemplateNodes: function(rootNode){
// summary:
// Iterate through the dom nodes and attach functions and nodes accordingly.
// description:
// Map widget properties and functions to the handlers specified in
// the dom node and it's descendants. This function iterates over all
// nodes and looks for these properties:
//
// - dojoAttachPoint/data-dojo-attach-point
// - dojoAttachEvent/data-dojo-attach-event
// rootNode: DomNode
// The node to search for properties. All descendants will be searched.
// tags:
// private
// DFS to process all nodes except those inside of this.containerNode
var node = rootNode;
while(true){
if(node.nodeType == 1 && (this._processTemplateNode(node, function(n,p){ return n.getAttribute(p); },
this._attach) || this.searchContainerNode) && node.firstChild){
node = node.firstChild;
}else{
if(node == rootNode){ return; }
while(!node.nextSibling){
node = node.parentNode;
if(node == rootNode){ return; }
}
node = node.nextSibling;
}
}
},
_processTemplateNode: function(/*DOMNode|Widget*/ baseNode, getAttrFunc, attachFunc){
// summary:
// Process data-dojo-attach-point and data-dojo-attach-event for given node or widget.
// Returns true if caller should process baseNode's children too.
var ret = true;
// Process data-dojo-attach-point
var _attachScope = this.attachScope || this,
attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
if(attachPoint){
var point, points = attachPoint.split(/\s*,\s*/);
while((point = points.shift())){
if(lang.isArray(_attachScope[point])){
_attachScope[point].push(baseNode);
}else{
_attachScope[point] = baseNode;
}
ret = (point != "containerNode");
this._attachPoints.push(point);
}
}
// Process data-dojo-attach-event
var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
if(attachEvent){
// NOTE: we want to support attributes that have the form
// "domEvent: nativeEvent; ..."
var event, events = attachEvent.split(/\s*,\s*/);
var trim = lang.trim;
while((event = events.shift())){
if(event){
var thisFunc = null;
if(event.indexOf(":") != -1){
// oh, if only JS had tuple assignment
var funcNameArr = event.split(":");
event = trim(funcNameArr[0]);
thisFunc = trim(funcNameArr[1]);
}else{
event = trim(event);
}
if(!thisFunc){
thisFunc = event;
}
this._attachEvents.push(attachFunc(baseNode, event, lang.hitch(_attachScope, thisFunc)));
}
}
}
return ret;
},
_attach: function(node, type, func){
// summary:
// Roughly corresponding to dojo/on, this is the default function for processing a
// data-dojo-attach-event. Meant to attach to DOMNodes, not to widgets.
// node: DOMNode
// The node to setup a listener on.
// type: String
// Event name like "click".
// getAttrFunc: Function
// Function to get the specified property for a given DomNode/Widget.
// attachFunc: Function?
// Attaches an event handler from the specified node/widget to specified function.
// Map special type names like "mouseenter" to synthetic events.
// Subclasses are responsible to require() dijit/a11yclick if they want to use it.
type = type.replace(/^on/, "").toLowerCase();
if(type == "dijitclick"){
type = a11yclick || (a11yclick = require("./a11yclick"));
}else{
type = synthEvents[type] || type;
}
return on(node, type, func);
},
_detachTemplateNodes: function() {
// summary:
// Detach and clean up the attachments made in _attachtempalteNodes.
// Delete all attach points to prevent IE6 memory leaks.
var _attachScope = this.attachScope || this;
array.forEach(this._attachPoints, function(point){
delete _attachScope[point];
});
this._attachPoints = [];
// And same for event handlers
array.forEach(this._attachEvents, function(handle){ handle.remove(); });
this._attachEvents = [];
},
destroyRendering: function(){
this._detachTemplateNodes();
this.inherited(arguments);
}
});
// These arguments can be specified for widgets which are used in templates.
// Since any widget can be specified as sub widgets in template, mix it
// into the base widget class. (This is a hack, but it's effective.).
// Remove for 2.0. Also, hide from API doc parser.
lang.extend(_WidgetBase, /*===== {} || =====*/ {
dojoAttachEvent: "",
dojoAttachPoint: ""
});
return _AttachMixin;
});
@@ -1,53 +1,159 @@
//>>built
define("dijit/_BidiMixin",[],function(){
var _1={LRM:"‎",LRE:"‪",PDF:"‬",RLM:"‏",RLE:"‫"};
return {textDir:"",getTextDir:function(_2){
return this.textDir=="auto"?this._checkContextual(_2):this.textDir;
},_checkContextual:function(_3){
var _4=/[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(_3);
return _4?(_4[0]<="z"?"ltr":"rtl"):this.dir?this.dir:this.isLeftToRight()?"ltr":"rtl";
},applyTextDir:function(_5,_6){
if(this.textDir){
var _7=this.textDir;
if(_7=="auto"){
if(typeof _6==="undefined"){
var _8=_5.tagName.toLowerCase();
_6=(_8=="input"||_8=="textarea")?_5.value:_5.innerText||_5.textContent||"";
}
_7=this._checkContextual(_6);
}
if(_5.dir!=_7){
_5.dir=_7;
}
}
},enforceTextDirWithUcc:function(_9,_a){
if(this.textDir){
if(_9){
_9.originalText=_a;
}
var _b=this.textDir=="auto"?this._checkContextual(_a):this.textDir;
return (_b=="ltr"?_1.LRE:_1.RLE)+_a+_1.PDF;
}
return _a;
},restoreOriginalText:function(_c){
if(_c.originalText){
_c.text=_c.originalText;
delete _c.originalText;
}
return _c;
},_setTextDirAttr:function(_d){
if(!this._created||this.textDir!=_d){
this._set("textDir",_d);
var _e=null;
if(this.displayNode){
_e=this.displayNode;
this.displayNode.align=this.dir=="rtl"?"right":"left";
}else{
_e=this.textDirNode||this.focusNode||this.textbox;
}
if(_e){
this.applyTextDir(_e);
}
}
}};
define([], function(){
// module:
// dijit/_BidiMixin
// UCC - constants that will be used by bidi support.
var bidi_const = {
LRM : '\u200E',
LRE : '\u202A',
PDF : '\u202C',
RLM : '\u200f',
RLE : '\u202B'
};
return {
// summary:
// When has("dojo-bidi") is true, _WidgetBase will mixin this class. It enables support for the textdir
// property to control text direction independently from the GUI direction.
// description:
// There's a special need for displaying BIDI text in rtl direction
// in ltr GUI, sometimes needed auto support.
// In creation of widget, if it's want to activate this class,
// the widget should define the "textDir".
// textDir: String
// Bi-directional support, the main variable which is responsible for the direction of the text.
// The text direction can be different than the GUI direction by using this parameter in creation
// of a widget.
//
// Allowed values:
//
// 1. "ltr"
// 2. "rtl"
// 3. "auto" - contextual the direction of a text defined by first strong letter.
//
// By default is as the page direction.
textDir: "",
getTextDir: function(/*String*/ text){
// summary:
// Gets the right direction of text.
// description:
// If textDir is ltr or rtl returns the value.
// If it's auto, calls to another function that responsible
// for checking the value, and defining the direction.
// tags:
// protected.
return this.textDir == "auto" ? this._checkContextual(text) : this.textDir;
},
_checkContextual: function(text){
// summary:
// Finds the first strong (directional) character, return ltr if isLatin
// or rtl if isBidiChar.
// tags:
// private.
// look for strong (directional) characters
var fdc = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(text);
// if found return the direction that defined by the character, else return widgets dir as defult.
return fdc ? ( fdc[0] <= 'z' ? "ltr" : "rtl" ) : this.dir ? this.dir : this.isLeftToRight() ? "ltr" : "rtl";
},
applyTextDir: function(/*DOMNode*/ element, /*String?*/ text){
// summary:
// Set element.dir according to this.textDir, assuming this.textDir has a value.
// element:
// The text element to be set. Should have dir property.
// text:
// If specified, and this.textDir is "auto", for calculating the right transformation
// Otherwise text read from element.
// description:
// If textDir is ltr or rtl returns the value.
// If it's auto, calls to another function that responsible
// for checking the value, and defining the direction.
// tags:
// protected.
if(this.textDir){
var textDir = this.textDir;
if(textDir == "auto"){
// convert "auto" to either "ltr" or "rtl"
if(typeof text === "undefined"){
// text not specified, get text from element
var tagName = element.tagName.toLowerCase();
text = (tagName == "input" || tagName == "textarea") ? element.value :
element.innerText || element.textContent || "";
}
textDir = this._checkContextual(text);
}
if(element.dir != textDir){
// set element's dir to match textDir, but not when textDir is null and not when it already matches
element.dir = textDir;
}
}
},
enforceTextDirWithUcc: function(option, text){
// summary:
// Wraps by UCC (Unicode control characters) option's text according to this.textDir
// option:
// The element (`<option>`) we wrapping the text for.
// text:
// The text to be wrapped.
// description:
// There's a dir problem with some HTML elements. For some elements (e.g. `<option>`, `<select>`)
// defining the dir in different direction then the GUI orientation, won't display correctly.
// FF 3.6 will change the alignment of the text in option - this doesn't follow the bidi standards (static text
// should be aligned following GUI direction). IE8 and Opera11.10 completely ignore dir setting for `<option>`.
// Therefore the only solution is to use UCC (Unicode control characters) to display the text in correct orientation.
// This function saves the original text value for later restoration if needed, for example if the textDir will change etc.
if(this.textDir){
if(option){
option.originalText = text;
}
var dir = this.textDir == "auto" ? this._checkContextual(text) : this.textDir;
return (dir == "ltr" ? bidi_const.LRE : bidi_const.RLE ) + text + bidi_const.PDF;
}
return text;
},
restoreOriginalText: function(origObj){
// summary:
// Restores the text of origObj, if needed, after enforceTextDirWithUcc, e.g. set("textDir", textDir).
// origObj:
// The element (`<option>`) to restore.
// description:
// Sets the text of origObj to origObj.originalText, which is the original text, without the UCCs.
// The function than removes the originalText from origObj!
if(origObj.originalText){
origObj.text = origObj.originalText;
delete origObj.originalText;
}
return origObj;
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
var node = null;
if(this.displayNode){
node = this.displayNode;
this.displayNode.align = this.dir == "rtl" ? "right" : "left";
}else{
node = this.textDirNode || this.focusNode || this.textbox;
}
if(node){
this.applyTextDir(node);
}
}
}
};
});
@@ -1,159 +0,0 @@
define("dijit/_BidiMixin", [], function(){
// module:
// dijit/_BidiMixin
// UCC - constants that will be used by bidi support.
var bidi_const = {
LRM : '\u200E',
LRE : '\u202A',
PDF : '\u202C',
RLM : '\u200f',
RLE : '\u202B'
};
return {
// summary:
// When has("dojo-bidi") is true, _WidgetBase will mixin this class. It enables support for the textdir
// property to control text direction independently from the GUI direction.
// description:
// There's a special need for displaying BIDI text in rtl direction
// in ltr GUI, sometimes needed auto support.
// In creation of widget, if it's want to activate this class,
// the widget should define the "textDir".
// textDir: String
// Bi-directional support, the main variable which is responsible for the direction of the text.
// The text direction can be different than the GUI direction by using this parameter in creation
// of a widget.
//
// Allowed values:
//
// 1. "ltr"
// 2. "rtl"
// 3. "auto" - contextual the direction of a text defined by first strong letter.
//
// By default is as the page direction.
textDir: "",
getTextDir: function(/*String*/ text){
// summary:
// Gets the right direction of text.
// description:
// If textDir is ltr or rtl returns the value.
// If it's auto, calls to another function that responsible
// for checking the value, and defining the direction.
// tags:
// protected.
return this.textDir == "auto" ? this._checkContextual(text) : this.textDir;
},
_checkContextual: function(text){
// summary:
// Finds the first strong (directional) character, return ltr if isLatin
// or rtl if isBidiChar.
// tags:
// private.
// look for strong (directional) characters
var fdc = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(text);
// if found return the direction that defined by the character, else return widgets dir as defult.
return fdc ? ( fdc[0] <= 'z' ? "ltr" : "rtl" ) : this.dir ? this.dir : this.isLeftToRight() ? "ltr" : "rtl";
},
applyTextDir: function(/*DOMNode*/ element, /*String?*/ text){
// summary:
// Set element.dir according to this.textDir, assuming this.textDir has a value.
// element:
// The text element to be set. Should have dir property.
// text:
// If specified, and this.textDir is "auto", for calculating the right transformation
// Otherwise text read from element.
// description:
// If textDir is ltr or rtl returns the value.
// If it's auto, calls to another function that responsible
// for checking the value, and defining the direction.
// tags:
// protected.
if(this.textDir){
var textDir = this.textDir;
if(textDir == "auto"){
// convert "auto" to either "ltr" or "rtl"
if(typeof text === "undefined"){
// text not specified, get text from element
var tagName = element.tagName.toLowerCase();
text = (tagName == "input" || tagName == "textarea") ? element.value :
element.innerText || element.textContent || "";
}
textDir = this._checkContextual(text);
}
if(element.dir != textDir){
// set element's dir to match textDir, but not when textDir is null and not when it already matches
element.dir = textDir;
}
}
},
enforceTextDirWithUcc: function(option, text){
// summary:
// Wraps by UCC (Unicode control characters) option's text according to this.textDir
// option:
// The element (`<option>`) we wrapping the text for.
// text:
// The text to be wrapped.
// description:
// There's a dir problem with some HTML elements. For some elements (e.g. `<option>`, `<select>`)
// defining the dir in different direction then the GUI orientation, won't display correctly.
// FF 3.6 will change the alignment of the text in option - this doesn't follow the bidi standards (static text
// should be aligned following GUI direction). IE8 and Opera11.10 completely ignore dir setting for `<option>`.
// Therefore the only solution is to use UCC (Unicode control characters) to display the text in correct orientation.
// This function saves the original text value for later restoration if needed, for example if the textDir will change etc.
if(this.textDir){
if(option){
option.originalText = text;
}
var dir = this.textDir == "auto" ? this._checkContextual(text) : this.textDir;
return (dir == "ltr" ? bidi_const.LRE : bidi_const.RLE ) + text + bidi_const.PDF;
}
return text;
},
restoreOriginalText: function(origObj){
// summary:
// Restores the text of origObj, if needed, after enforceTextDirWithUcc, e.g. set("textDir", textDir).
// origObj:
// The element (`<option>`) to restore.
// description:
// Sets the text of origObj to origObj.originalText, which is the original text, without the UCCs.
// The function than removes the originalText from origObj!
if(origObj.originalText){
origObj.text = origObj.originalText;
delete origObj.originalText;
}
return origObj;
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
var node = null;
if(this.displayNode){
node = this.displayNode;
this.displayNode.align = this.dir == "rtl" ? "right" : "left";
}else{
node = this.textDirNode || this.focusNode || this.textbox;
}
if(node){
this.applyTextDir(node);
}
}
}
};
});
@@ -1,6 +1,21 @@
//>>built
define("dijit/_BidiSupport",["dojo/has","./_WidgetBase","./_BidiMixin"],function(_1,_2,_3){
_2.extend(_3);
_1.add("dojo-bidi",true);
return _2;
define(["dojo/has", "./_WidgetBase", "./_BidiMixin"], function(has, _WidgetBase, _BidiMixin){
// module:
// dijit/_BidiSupport
/*=====
return function(){
// summary:
// Deprecated module for enabling textdir support in the dijit widgets. New code should just define
// has("dojo-bidi") to return true, rather than manually requiring this module.
};
=====*/
_WidgetBase.extend(_BidiMixin);
// Back-compat with version 1.8: just including _BidiSupport should trigger bidi support in all the widgets.
// Although this statement doesn't do much because the other widgets have likely already been loaded.
has.add("dojo-bidi", true);
return _WidgetBase;
});
@@ -1,21 +0,0 @@
define("dijit/_BidiSupport", ["dojo/has", "./_WidgetBase", "./_BidiMixin"], function(has, _WidgetBase, _BidiMixin){
// module:
// dijit/_BidiSupport
/*=====
return function(){
// summary:
// Deprecated module for enabling textdir support in the dijit widgets. New code should just define
// has("dojo-bidi") to return true, rather than manually requiring this module.
};
=====*/
_WidgetBase.extend(_BidiMixin);
// Back-compat with version 1.8: just including _BidiSupport should trigger bidi support in all the widgets.
// Although this statement doesn't do much because the other widgets have likely already been loaded.
has.add("dojo-bidi", true);
return _WidgetBase;
});
@@ -1,5 +1,22 @@
//>>built
define("dijit/_Calendar",["dojo/_base/kernel","./Calendar","./main"],function(_1,_2,_3){
_1.deprecated("dijit._Calendar is deprecated","dijit._Calendar moved to dijit.Calendar",2);
_3._Calendar=_2;
define([
"dojo/_base/kernel", // kernel.deprecated
"./Calendar",
"./main" // for exporting dijit.Calendar
], function(kernel, Calendar, dijit){
// module:
// dijit/_Calendar
/*=====
return {
// summary:
// Deprecated widget, used dijit/Calendar instead. Will be removed in 2.0.
};
=====*/
kernel.deprecated("dijit._Calendar is deprecated", "dijit._Calendar moved to dijit.Calendar", 2.0);
// dijit._Calendar had an underscore all this time merely because it did
// not satisfy dijit's a11y policy.
dijit._Calendar = Calendar;
});
@@ -1,22 +0,0 @@
define("dijit/_Calendar", [
"dojo/_base/kernel", // kernel.deprecated
"./Calendar",
"./main" // for exporting dijit.Calendar
], function(kernel, Calendar, dijit){
// module:
// dijit/_Calendar
/*=====
return {
// summary:
// Deprecated widget, used dijit/Calendar instead. Will be removed in 2.0.
};
=====*/
kernel.deprecated("dijit._Calendar is deprecated", "dijit._Calendar moved to dijit.Calendar", 2.0);
// dijit._Calendar had an underscore all this time merely because it did
// not satisfy dijit's a11y policy.
dijit._Calendar = Calendar;
});
@@ -1,5 +1,26 @@
//>>built
require({cache:{"url:dijit/templates/actionBar.html":"<div class='dijitDialogPaneActionBar' data-dojo-attach-point=\"actionBarNode\">\n\t<button data-dojo-type='dijit/form/Button' type='submit' data-dojo-attach-point=\"okButton\"></button>\n\t<button data-dojo-type='dijit/form/Button' type='button'\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event='click:onCancel'></button>\n</div>\n"}});
define("dijit/_ConfirmDialogMixin",["dojo/_base/declare","./_WidgetsInTemplateMixin","dojo/i18n!./nls/common","dojo/text!./templates/actionBar.html","./form/Button"],function(_1,_2,_3,_4){
return _1("dijit/_ConfirmDialogMixin",_2,{actionBarTemplate:_4,buttonOk:_3.buttonOk,_setButtonOkAttr:{node:"okButton",attribute:"label"},buttonCancel:_3.buttonCancel,_setButtonCancelAttr:{node:"cancelButton",attribute:"label"}});
define([
"dojo/_base/declare",
"./_WidgetsInTemplateMixin",
"dojo/i18n!./nls/common",
"dojo/text!./templates/actionBar.html",
"./form/Button" // used by template
], function(declare, _WidgetsInTemplateMixin, strings, actionBarMarkup) {
return declare("dijit/_ConfirmDialogMixin", _WidgetsInTemplateMixin, {
// summary:
// Mixin for Dialog/TooltipDialog with OK/Cancel buttons.
// HTML snippet for action bar, overrides _DialogMixin.actionBarTemplate
actionBarTemplate: actionBarMarkup,
// buttonOk: String
// Label of OK button
buttonOk: strings.buttonOk,
_setButtonOkAttr: { node: "okButton", attribute: "label" },
// buttonCancel: String
// Label of cancel button
buttonCancel: strings.buttonCancel,
_setButtonCancelAttr: { node: "cancelButton", attribute: "label" }
});
});
@@ -1,28 +0,0 @@
require({cache:{
'url:dijit/templates/actionBar.html':"<div class='dijitDialogPaneActionBar' data-dojo-attach-point=\"actionBarNode\">\n\t<button data-dojo-type='dijit/form/Button' type='submit' data-dojo-attach-point=\"okButton\"></button>\n\t<button data-dojo-type='dijit/form/Button' type='button'\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event='click:onCancel'></button>\n</div>\n"}});
define("dijit/_ConfirmDialogMixin", [
"dojo/_base/declare",
"./_WidgetsInTemplateMixin",
"dojo/i18n!./nls/common",
"dojo/text!./templates/actionBar.html",
"./form/Button" // used by template
], function(declare, _WidgetsInTemplateMixin, strings, actionBarMarkup) {
return declare("dijit/_ConfirmDialogMixin", _WidgetsInTemplateMixin, {
// summary:
// Mixin for Dialog/TooltipDialog with OK/Cancel buttons.
// HTML snippet for action bar, overrides _DialogMixin.actionBarTemplate
actionBarTemplate: actionBarMarkup,
// buttonOk: String
// Label of OK button
buttonOk: strings.buttonOk,
_setButtonOkAttr: { node: "okButton", attribute: "label" },
// buttonCancel: String
// Label of cancel button
buttonCancel: strings.buttonCancel,
_setButtonCancelAttr: { node: "cancelButton", attribute: "label" }
});
});
@@ -1,20 +1,59 @@
//>>built
define("dijit/_Contained",["dojo/_base/declare","./registry"],function(_1,_2){
return _1("dijit._Contained",null,{_getSibling:function(_3){
var _4=this.domNode;
do{
_4=_4[_3+"Sibling"];
}while(_4&&_4.nodeType!=1);
return _4&&_2.byNode(_4);
},getPreviousSibling:function(){
return this._getSibling("previous");
},getNextSibling:function(){
return this._getSibling("next");
},getIndexInParent:function(){
var p=this.getParent();
if(!p||!p.getIndexOfChild){
return -1;
}
return p.getIndexOfChild(this);
}});
define([
"dojo/_base/declare", // declare
"./registry" // registry.getEnclosingWidget(), registry.byNode()
], function(declare, registry){
// module:
// dijit/_Contained
return declare("dijit._Contained", null, {
// summary:
// Mixin for widgets that are children of a container widget
// example:
// | // make a basic custom widget that knows about its parents
// | declare("my.customClass",[dijit._WidgetBase, dijit._Contained],{});
_getSibling: function(/*String*/ which){
// summary:
// Returns next or previous sibling
// which:
// Either "next" or "previous"
// tags:
// private
var node = this.domNode;
do{
node = node[which+"Sibling"];
}while(node && node.nodeType != 1);
return node && registry.byNode(node); // dijit/_WidgetBase
},
getPreviousSibling: function(){
// summary:
// Returns null if this is the first child of the parent,
// otherwise returns the next element sibling to the "left".
return this._getSibling("previous"); // dijit/_WidgetBase
},
getNextSibling: function(){
// summary:
// Returns null if this is the last child of the parent,
// otherwise returns the next element sibling to the "right".
return this._getSibling("next"); // dijit/_WidgetBase
},
getIndexInParent: function(){
// summary:
// Returns the index of this widget within its container parent.
// It returns -1 if the parent does not exist, or if the parent
// is not a dijit/_Container
var p = this.getParent();
if(!p || !p.getIndexOfChild){
return -1; // int
}
return p.getIndexOfChild(this); // int
}
});
});
@@ -1,59 +0,0 @@
define("dijit/_Contained", [
"dojo/_base/declare", // declare
"./registry" // registry.getEnclosingWidget(), registry.byNode()
], function(declare, registry){
// module:
// dijit/_Contained
return declare("dijit._Contained", null, {
// summary:
// Mixin for widgets that are children of a container widget
// example:
// | // make a basic custom widget that knows about its parents
// | declare("my.customClass",[dijit._WidgetBase, dijit._Contained],{});
_getSibling: function(/*String*/ which){
// summary:
// Returns next or previous sibling
// which:
// Either "next" or "previous"
// tags:
// private
var node = this.domNode;
do{
node = node[which+"Sibling"];
}while(node && node.nodeType != 1);
return node && registry.byNode(node); // dijit/_WidgetBase
},
getPreviousSibling: function(){
// summary:
// Returns null if this is the first child of the parent,
// otherwise returns the next element sibling to the "left".
return this._getSibling("previous"); // dijit/_WidgetBase
},
getNextSibling: function(){
// summary:
// Returns null if this is the last child of the parent,
// otherwise returns the next element sibling to the "right".
return this._getSibling("next"); // dijit/_WidgetBase
},
getIndexInParent: function(){
// summary:
// Returns the index of this widget within its container parent.
// It returns -1 if the parent does not exist, or if the parent
// is not a dijit/_Container
var p = this.getParent();
if(!p || !p.getIndexOfChild){
return -1; // int
}
return p.getIndexOfChild(this); // int
}
});
});
@@ -1,48 +1,109 @@
//>>built
define("dijit/_Container",["dojo/_base/array","dojo/_base/declare","dojo/dom-construct","dojo/_base/kernel"],function(_1,_2,_3,_4){
return _2("dijit._Container",null,{buildRendering:function(){
this.inherited(arguments);
if(!this.containerNode){
this.containerNode=this.domNode;
}
},addChild:function(_5,_6){
var _7=this.containerNode;
if(_6>0){
_7=_7.firstChild;
while(_6>0){
if(_7.nodeType==1){
_6--;
}
_7=_7.nextSibling;
}
if(_7){
_6="before";
}else{
_7=this.containerNode;
_6="last";
}
}
_3.place(_5.domNode,_7,_6);
if(this._started&&!_5._started){
_5.startup();
}
},removeChild:function(_8){
if(typeof _8=="number"){
_8=this.getChildren()[_8];
}
if(_8){
var _9=_8.domNode;
if(_9&&_9.parentNode){
_9.parentNode.removeChild(_9);
}
}
},hasChildren:function(){
return this.getChildren().length>0;
},_getSiblingOfChild:function(_a,_b){
_4.deprecated(this.declaredClass+"::_getSiblingOfChild() is deprecated. Use _KeyNavMixin::_getNext() instead.","","2.0");
var _c=this.getChildren(),_d=_1.indexOf(_c,_a);
return _c[_d+_b];
},getIndexOfChild:function(_e){
return _1.indexOf(this.getChildren(),_e);
}});
define([
"dojo/_base/array", // array.forEach array.indexOf
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/_base/kernel" // kernel.deprecated
], function(array, declare, domConstruct, kernel){
// module:
// dijit/_Container
return declare("dijit._Container", null, {
// summary:
// Mixin for widgets that contain HTML and/or a set of widget children.
buildRendering: function(){
this.inherited(arguments);
if(!this.containerNode){
// All widgets with descendants must set containerNode.
// NB: this code doesn't quite work right because for TabContainer it runs before
// _TemplatedMixin::buildRendering(), and thus
// sets this.containerNode to this.domNode, later to be overridden by the assignment in the template.
this.containerNode = this.domNode;
}
},
addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
// summary:
// Makes the given widget a child of this widget.
// description:
// Inserts specified child widget's dom node as a child of this widget's
// container node, and possibly does other processing (such as layout).
// I want to just call domConstruct.place(widget.domNode, this.containerNode, insertIndex), but the counting
// is thrown off by text nodes and comment nodes that show up when constructed by markup.
// In the future consider stripping those nodes on construction, either in the parser or this widget code.
var refNode = this.containerNode;
if(insertIndex > 0){
// Old-school way to get nth child; dojo.query would be easier but _Container was weened from dojo.query
// in #10087 to minimize download size. Not sure if that's still and issue with new smaller dojo/query.
refNode = refNode.firstChild;
while(insertIndex > 0){
if(refNode.nodeType == 1){ insertIndex--; }
refNode = refNode.nextSibling;
}
if(refNode){
insertIndex = "before";
}else{
// to support addChild(child, n-1) where there are n children (should add child at end)
refNode = this.containerNode;
insertIndex = "last";
}
}
domConstruct.place(widget.domNode, refNode, insertIndex);
// If I've been started but the child widget hasn't been started,
// start it now. Make sure to do this after widget has been
// inserted into the DOM tree, so it can see that it's being controlled by me,
// so it doesn't try to size itself.
if(this._started && !widget._started){
widget.startup();
}
},
removeChild: function(/*Widget|int*/ widget){
// summary:
// Removes the passed widget instance from this widget but does
// not destroy it. You can also pass in an integer indicating
// the index within the container to remove (ie, removeChild(5) removes the sixth widget).
if(typeof widget == "number"){
widget = this.getChildren()[widget];
}
if(widget){
var node = widget.domNode;
if(node && node.parentNode){
node.parentNode.removeChild(node); // detach but don't destroy
}
}
},
hasChildren: function(){
// summary:
// Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
return this.getChildren().length > 0; // Boolean
},
_getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir){
// summary:
// Get the next or previous widget sibling of child
// dir:
// if 1, get the next sibling
// if -1, get the previous sibling
// tags:
// private
kernel.deprecated(this.declaredClass+"::_getSiblingOfChild() is deprecated. Use _KeyNavMixin::_getNext() instead.", "", "2.0");
var children = this.getChildren(),
idx = array.indexOf(children, child); // int
return children[idx + dir];
},
getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
// summary:
// Gets the index of the child in this container or -1 if not found
return array.indexOf(this.getChildren(), child); // int
}
});
});
@@ -1,109 +0,0 @@
define("dijit/_Container", [
"dojo/_base/array", // array.forEach array.indexOf
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/_base/kernel" // kernel.deprecated
], function(array, declare, domConstruct, kernel){
// module:
// dijit/_Container
return declare("dijit._Container", null, {
// summary:
// Mixin for widgets that contain HTML and/or a set of widget children.
buildRendering: function(){
this.inherited(arguments);
if(!this.containerNode){
// All widgets with descendants must set containerNode.
// NB: this code doesn't quite work right because for TabContainer it runs before
// _TemplatedMixin::buildRendering(), and thus
// sets this.containerNode to this.domNode, later to be overridden by the assignment in the template.
this.containerNode = this.domNode;
}
},
addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
// summary:
// Makes the given widget a child of this widget.
// description:
// Inserts specified child widget's dom node as a child of this widget's
// container node, and possibly does other processing (such as layout).
// I want to just call domConstruct.place(widget.domNode, this.containerNode, insertIndex), but the counting
// is thrown off by text nodes and comment nodes that show up when constructed by markup.
// In the future consider stripping those nodes on construction, either in the parser or this widget code.
var refNode = this.containerNode;
if(insertIndex > 0){
// Old-school way to get nth child; dojo.query would be easier but _Container was weened from dojo.query
// in #10087 to minimize download size. Not sure if that's still and issue with new smaller dojo/query.
refNode = refNode.firstChild;
while(insertIndex > 0){
if(refNode.nodeType == 1){ insertIndex--; }
refNode = refNode.nextSibling;
}
if(refNode){
insertIndex = "before";
}else{
// to support addChild(child, n-1) where there are n children (should add child at end)
refNode = this.containerNode;
insertIndex = "last";
}
}
domConstruct.place(widget.domNode, refNode, insertIndex);
// If I've been started but the child widget hasn't been started,
// start it now. Make sure to do this after widget has been
// inserted into the DOM tree, so it can see that it's being controlled by me,
// so it doesn't try to size itself.
if(this._started && !widget._started){
widget.startup();
}
},
removeChild: function(/*Widget|int*/ widget){
// summary:
// Removes the passed widget instance from this widget but does
// not destroy it. You can also pass in an integer indicating
// the index within the container to remove (ie, removeChild(5) removes the sixth widget).
if(typeof widget == "number"){
widget = this.getChildren()[widget];
}
if(widget){
var node = widget.domNode;
if(node && node.parentNode){
node.parentNode.removeChild(node); // detach but don't destroy
}
}
},
hasChildren: function(){
// summary:
// Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
return this.getChildren().length > 0; // Boolean
},
_getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir){
// summary:
// Get the next or previous widget sibling of child
// dir:
// if 1, get the next sibling
// if -1, get the previous sibling
// tags:
// private
kernel.deprecated(this.declaredClass+"::_getSiblingOfChild() is deprecated. Use _KeyNavMixin::_getNext() instead.", "", "2.0");
var children = this.getChildren(),
idx = array.indexOf(children, child); // int
return children[idx + dir];
},
getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
// summary:
// Gets the index of the child in this container or -1 if not found
return array.indexOf(this.getChildren(), child); // int
}
});
});
@@ -1,198 +1,372 @@
//>>built
define("dijit/_CssStateMixin",["dojo/_base/array","dojo/_base/declare","dojo/dom","dojo/dom-class","dojo/has","dojo/_base/lang","dojo/on","dojo/domReady","dojo/touch","dojo/_base/window","./a11yclick","./registry"],function(_1,_2,_3,_4,_5,_6,on,_7,_8,_9,_a,_b){
var _c=_2("dijit._CssStateMixin",[],{hovering:false,active:false,_applyAttributes:function(){
this.inherited(arguments);
_1.forEach(["disabled","readOnly","checked","selected","focused","state","hovering","active","_opened"],function(_d){
this.watch(_d,_6.hitch(this,"_setStateClass"));
},this);
for(var ap in this.cssStateNodes||{}){
this._trackMouseState(this[ap],this.cssStateNodes[ap]);
}
this._trackMouseState(this.domNode,this.baseClass);
this._setStateClass();
},_cssMouseEvent:function(_e){
if(!this.disabled){
switch(_e.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
this._set("hovering",true);
this._set("active",this._mouseDown);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
this._set("hovering",false);
this._set("active",false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
this._set("active",true);
break;
case "mouseup":
case "dojotouchend":
case "MSPointerUp":
case "pointerup":
case "keyup":
this._set("active",false);
break;
}
}
},_setStateClass:function(){
var _f=this.baseClass.split(" ");
function _10(_11){
_f=_f.concat(_1.map(_f,function(c){
return c+_11;
}),"dijit"+_11);
};
if(!this.isLeftToRight()){
_10("Rtl");
}
var _12=this.checked=="mixed"?"Mixed":(this.checked?"Checked":"");
if(this.checked){
_10(_12);
}
if(this.state){
_10(this.state);
}
if(this.selected){
_10("Selected");
}
if(this._opened){
_10("Opened");
}
if(this.disabled){
_10("Disabled");
}else{
if(this.readOnly){
_10("ReadOnly");
}else{
if(this.active){
_10("Active");
}else{
if(this.hovering){
_10("Hover");
}
}
}
}
if(this.focused){
_10("Focused");
}
var tn=this.stateNode||this.domNode,_13={};
_1.forEach(tn.className.split(" "),function(c){
_13[c]=true;
});
if("_stateClasses" in this){
_1.forEach(this._stateClasses,function(c){
delete _13[c];
});
}
_1.forEach(_f,function(c){
_13[c]=true;
});
var _14=[];
for(var c in _13){
_14.push(c);
}
tn.className=_14.join(" ");
this._stateClasses=_f;
},_subnodeCssMouseEvent:function(_15,_16,evt){
if(this.disabled||this.readOnly){
return;
}
function _17(_18){
_4.toggle(_15,_16+"Hover",_18);
};
function _19(_1a){
_4.toggle(_15,_16+"Active",_1a);
};
function _1b(_1c){
_4.toggle(_15,_16+"Focused",_1c);
};
switch(evt.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
_17(true);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
_17(false);
_19(false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
_19(true);
break;
case "mouseup":
case "MSPointerUp":
case "pointerup":
case "dojotouchend":
case "keyup":
_19(false);
break;
case "focus":
case "focusin":
_1b(true);
break;
case "blur":
case "focusout":
_1b(false);
break;
}
},_trackMouseState:function(_1d,_1e){
_1d._cssState=_1e;
}});
_7(function(){
function _1f(evt,_20,_21){
if(_21&&_3.isDescendant(_21,_20)){
return;
}
for(var _22=_20;_22&&_22!=_21;_22=_22.parentNode){
if(_22._cssState){
var _23=_b.getEnclosingWidget(_22);
if(_23){
if(_22==_23.domNode){
_23._cssMouseEvent(evt);
}else{
_23._subnodeCssMouseEvent(_22,_22._cssState,evt);
}
}
}
}
};
var _24=_9.body(),_25;
on(_24,_8.over,function(evt){
_1f(evt,evt.target,evt.relatedTarget);
});
on(_24,_8.out,function(evt){
_1f(evt,evt.target,evt.relatedTarget);
});
on(_24,_a.press,function(evt){
_25=evt.target;
_1f(evt,_25);
});
on(_24,_a.release,function(evt){
_1f(evt,_25);
_25=null;
});
on(_24,"focusin, focusout",function(evt){
var _26=evt.target;
if(_26._cssState&&!_26.getAttribute("widgetId")){
var _27=_b.getEnclosingWidget(_26);
if(_27){
_27._subnodeCssMouseEvent(_26,_26._cssState,evt);
}
}
});
});
return _c;
define([
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant()
"dojo/dom-class", // domClass.toggle
"dojo/has",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/domReady",
"dojo/touch",
"dojo/_base/window", // win.body
"./a11yclick",
"./registry"
], function(array, declare, dom, domClass, has, lang, on, domReady, touch, win, a11yclick, registry){
// module:
// dijit/_CssStateMixin
var CssStateMixin = declare("dijit._CssStateMixin", [], {
// summary:
// Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
// state changes, and also higher-level state changes such becoming disabled or selected.
//
// description:
// By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
// maintain CSS classes on the widget root node (this.domNode) depending on hover,
// active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
// dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
//
// It also sets CSS like dijitButtonDisabled based on widget semantic state.
//
// By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
// within the widget).
/*=====
// cssStateNodes: [protected] Object
// Subclasses may define a cssStateNodes property that lists sub-nodes within the widget that
// need CSS classes applied on mouse hover/press and focus.
//
// Each entry in this optional hash is a an attach-point name (like "upArrowButton") mapped to a CSS class name
// (like "dijitUpArrowButton"). Example:
// | {
// | "upArrowButton": "dijitUpArrowButton",
// | "downArrowButton": "dijitDownArrowButton"
// | }
// The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
// is hovered, etc.
cssStateNodes: {},
=====*/
// hovering: [readonly] Boolean
// True if cursor is over this widget
hovering: false,
// active: [readonly] Boolean
// True if mouse was pressed while over this widget, and hasn't been released yet
active: false,
_applyAttributes: function(){
// This code would typically be in postCreate(), but putting in _applyAttributes() for
// performance: so the class changes happen before DOM is inserted into the document.
// Change back to postCreate() in 2.0. See #11635.
this.inherited(arguments);
// Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr){
this.watch(attr, lang.hitch(this, "_setStateClass"));
}, this);
// Track hover and active mouse events on widget root node, plus possibly on subnodes
for(var ap in this.cssStateNodes || {}){
this._trackMouseState(this[ap], this.cssStateNodes[ap]);
}
this._trackMouseState(this.domNode, this.baseClass);
// Set state initially; there's probably no hover/active/focus state but widget might be
// disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
this._setStateClass();
},
_cssMouseEvent: function(/*Event*/ event){
// summary:
// Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
// which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
if(!this.disabled){
switch(event.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
this._set("hovering", true);
this._set("active", this._mouseDown);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
this._set("hovering", false);
this._set("active", false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
this._set("active", true);
break;
case "mouseup":
case "dojotouchend":
case "MSPointerUp":
case "pointerup":
case "keyup":
this._set("active", false);
break;
}
}
},
_setStateClass: function(){
// summary:
// Update the visual state of the widget by setting the css classes on this.domNode
// (or this.stateNode if defined) by combining this.baseClass with
// various suffixes that represent the current widget state(s).
//
// description:
// In the case where a widget has multiple
// states, it sets the class based on all possible
// combinations. For example, an invalid form widget that is being hovered
// will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
//
// The widget may have one or more of the following states, determined
// by this.state, this.checked, this.valid, and this.selected:
//
// - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
// - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
// - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
// - Selected - ex: currently selected tab will have this.selected==true
//
// In addition, it may have one or more of the following states,
// based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
//
// - Disabled - if the widget is disabled
// - Active - if the mouse (or space/enter key?) is being pressed down
// - Focused - if the widget has focus
// - Hover - if the mouse is over the widget
// Compute new set of classes
var newStateClasses = this.baseClass.split(" ");
function multiply(modifier){
newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){
return c + modifier;
}), "dijit" + modifier);
}
if(!this.isLeftToRight()){
// For RTL mode we need to set an addition class like dijitTextBoxRtl.
multiply("Rtl");
}
var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
if(this.checked){
multiply(checkedState);
}
if(this.state){
multiply(this.state);
}
if(this.selected){
multiply("Selected");
}
if(this._opened){
multiply("Opened");
}
if(this.disabled){
multiply("Disabled");
}else if(this.readOnly){
multiply("ReadOnly");
}else{
if(this.active){
multiply("Active");
}else if(this.hovering){
multiply("Hover");
}
}
if(this.focused){
multiply("Focused");
}
// Remove old state classes and add new ones.
// For performance concerns we only write into domNode.className once.
var tn = this.stateNode || this.domNode,
classHash = {}; // set of all classes (state and otherwise) for node
array.forEach(tn.className.split(" "), function(c){
classHash[c] = true;
});
if("_stateClasses" in this){
array.forEach(this._stateClasses, function(c){
delete classHash[c];
});
}
array.forEach(newStateClasses, function(c){
classHash[c] = true;
});
var newClasses = [];
for(var c in classHash){
newClasses.push(c);
}
tn.className = newClasses.join(" ");
this._stateClasses = newStateClasses;
},
_subnodeCssMouseEvent: function(node, clazz, evt){
// summary:
// Handler for hover/active mouse event on widget's subnode
if(this.disabled || this.readOnly){
return;
}
function hover(isHovering){
domClass.toggle(node, clazz + "Hover", isHovering);
}
function active(isActive){
domClass.toggle(node, clazz + "Active", isActive);
}
function focused(isFocused){
domClass.toggle(node, clazz + "Focused", isFocused);
}
switch(evt.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
hover(true);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
hover(false);
active(false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
active(true);
break;
case "mouseup":
case "MSPointerUp":
case "pointerup":
case "dojotouchend":
case "keyup":
active(false);
break;
case "focus":
case "focusin":
focused(true);
break;
case "blur":
case "focusout":
focused(false);
break;
}
},
_trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
// summary:
// Track mouse/focus events on specified node and set CSS class on that node to indicate
// current state. Usually not called directly, but via cssStateNodes attribute.
// description:
// Given class=foo, will set the following CSS class on the node
//
// - fooActive: if the user is currently pressing down the mouse button while over the node
// - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
// - fooFocus: if the node is focused
//
// Note that it won't set any classes if the widget is disabled.
// node: DomNode
// Should be a sub-node of the widget, not the top node (this.domNode), since the top node
// is handled specially and automatically just by mixing in this class.
// clazz: String
// CSS class name (ex: dijitSliderUpArrow)
// Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
// when node is hovered/active
node._cssState = clazz;
}
});
domReady(function(){
// Document level listener to catch hover etc. events on widget root nodes and subnodes.
// Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
// have been hovered or unhovered (try test_Accordion.html)
function pointerHandler(evt, target, relatedTarget){
// Handler for mouseover, mouseout, a11yclick.press and a11click.release events
// Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
// to avoid processing mouseout events moving from a widget's domNode to a descendant node;
// such events shouldn't be interpreted as a mouseleave on the widget.
if(relatedTarget && dom.isDescendant(relatedTarget, target)){
return;
}
for(var node = target; node && node != relatedTarget; node = node.parentNode){
// Process any nodes with _cssState property. They are generally widget root nodes,
// but could also be sub-nodes within a widget
if(node._cssState){
var widget = registry.getEnclosingWidget(node);
if(widget){
if(node == widget.domNode){
// event on the widget's root node
widget._cssMouseEvent(evt);
}else{
// event on widget's sub-node
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
}
}
}
}
var body = win.body(), activeNode;
// Handle pointer related events (i.e. mouse or touch)
on(body, touch.over, function(evt){
// Using touch.over rather than mouseover mainly to ignore phantom mouse events on iOS.
pointerHandler(evt, evt.target, evt.relatedTarget);
});
on(body, touch.out, function(evt){
// Using touch.out rather than mouseout mainly to ignore phantom mouse events on iOS.
pointerHandler(evt, evt.target, evt.relatedTarget);
});
on(body, a11yclick.press, function(evt){
// Save the a11yclick.press target to reference when the a11yclick.release comes.
activeNode = evt.target;
pointerHandler(evt, activeNode)
});
on(body, a11yclick.release, function(evt){
// The release event could come on a separate node than the press event, if for example user slid finger.
// Reference activeNode to reset the state of the node that got state set in the a11yclick.press handler.
pointerHandler(evt, activeNode);
activeNode = null;
});
// Track focus events on widget sub-nodes that have been registered via _trackMouseState().
// However, don't track focus events on the widget root nodes, because focus is tracked via the
// focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
// nodes or a subwidget's node or a popup node, etc.)
// Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
on(body, "focusin, focusout", function(evt){
var node = evt.target;
if(node._cssState && !node.getAttribute("widgetId")){
var widget = registry.getEnclosingWidget(node);
if(widget){
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
}
});
});
return CssStateMixin;
});
@@ -1,372 +0,0 @@
define("dijit/_CssStateMixin", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant()
"dojo/dom-class", // domClass.toggle
"dojo/has",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/domReady",
"dojo/touch",
"dojo/_base/window", // win.body
"./a11yclick",
"./registry"
], function(array, declare, dom, domClass, has, lang, on, domReady, touch, win, a11yclick, registry){
// module:
// dijit/_CssStateMixin
var CssStateMixin = declare("dijit._CssStateMixin", [], {
// summary:
// Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
// state changes, and also higher-level state changes such becoming disabled or selected.
//
// description:
// By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
// maintain CSS classes on the widget root node (this.domNode) depending on hover,
// active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
// dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
//
// It also sets CSS like dijitButtonDisabled based on widget semantic state.
//
// By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
// within the widget).
/*=====
// cssStateNodes: [protected] Object
// Subclasses may define a cssStateNodes property that lists sub-nodes within the widget that
// need CSS classes applied on mouse hover/press and focus.
//
// Each entry in this optional hash is a an attach-point name (like "upArrowButton") mapped to a CSS class name
// (like "dijitUpArrowButton"). Example:
// | {
// | "upArrowButton": "dijitUpArrowButton",
// | "downArrowButton": "dijitDownArrowButton"
// | }
// The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
// is hovered, etc.
cssStateNodes: {},
=====*/
// hovering: [readonly] Boolean
// True if cursor is over this widget
hovering: false,
// active: [readonly] Boolean
// True if mouse was pressed while over this widget, and hasn't been released yet
active: false,
_applyAttributes: function(){
// This code would typically be in postCreate(), but putting in _applyAttributes() for
// performance: so the class changes happen before DOM is inserted into the document.
// Change back to postCreate() in 2.0. See #11635.
this.inherited(arguments);
// Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr){
this.watch(attr, lang.hitch(this, "_setStateClass"));
}, this);
// Track hover and active mouse events on widget root node, plus possibly on subnodes
for(var ap in this.cssStateNodes || {}){
this._trackMouseState(this[ap], this.cssStateNodes[ap]);
}
this._trackMouseState(this.domNode, this.baseClass);
// Set state initially; there's probably no hover/active/focus state but widget might be
// disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
this._setStateClass();
},
_cssMouseEvent: function(/*Event*/ event){
// summary:
// Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
// which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
if(!this.disabled){
switch(event.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
this._set("hovering", true);
this._set("active", this._mouseDown);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
this._set("hovering", false);
this._set("active", false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
this._set("active", true);
break;
case "mouseup":
case "dojotouchend":
case "MSPointerUp":
case "pointerup":
case "keyup":
this._set("active", false);
break;
}
}
},
_setStateClass: function(){
// summary:
// Update the visual state of the widget by setting the css classes on this.domNode
// (or this.stateNode if defined) by combining this.baseClass with
// various suffixes that represent the current widget state(s).
//
// description:
// In the case where a widget has multiple
// states, it sets the class based on all possible
// combinations. For example, an invalid form widget that is being hovered
// will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
//
// The widget may have one or more of the following states, determined
// by this.state, this.checked, this.valid, and this.selected:
//
// - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
// - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
// - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
// - Selected - ex: currently selected tab will have this.selected==true
//
// In addition, it may have one or more of the following states,
// based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
//
// - Disabled - if the widget is disabled
// - Active - if the mouse (or space/enter key?) is being pressed down
// - Focused - if the widget has focus
// - Hover - if the mouse is over the widget
// Compute new set of classes
var newStateClasses = this.baseClass.split(" ");
function multiply(modifier){
newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){
return c + modifier;
}), "dijit" + modifier);
}
if(!this.isLeftToRight()){
// For RTL mode we need to set an addition class like dijitTextBoxRtl.
multiply("Rtl");
}
var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
if(this.checked){
multiply(checkedState);
}
if(this.state){
multiply(this.state);
}
if(this.selected){
multiply("Selected");
}
if(this._opened){
multiply("Opened");
}
if(this.disabled){
multiply("Disabled");
}else if(this.readOnly){
multiply("ReadOnly");
}else{
if(this.active){
multiply("Active");
}else if(this.hovering){
multiply("Hover");
}
}
if(this.focused){
multiply("Focused");
}
// Remove old state classes and add new ones.
// For performance concerns we only write into domNode.className once.
var tn = this.stateNode || this.domNode,
classHash = {}; // set of all classes (state and otherwise) for node
array.forEach(tn.className.split(" "), function(c){
classHash[c] = true;
});
if("_stateClasses" in this){
array.forEach(this._stateClasses, function(c){
delete classHash[c];
});
}
array.forEach(newStateClasses, function(c){
classHash[c] = true;
});
var newClasses = [];
for(var c in classHash){
newClasses.push(c);
}
tn.className = newClasses.join(" ");
this._stateClasses = newStateClasses;
},
_subnodeCssMouseEvent: function(node, clazz, evt){
// summary:
// Handler for hover/active mouse event on widget's subnode
if(this.disabled || this.readOnly){
return;
}
function hover(isHovering){
domClass.toggle(node, clazz + "Hover", isHovering);
}
function active(isActive){
domClass.toggle(node, clazz + "Active", isActive);
}
function focused(isFocused){
domClass.toggle(node, clazz + "Focused", isFocused);
}
switch(evt.type){
case "mouseover":
case "MSPointerOver":
case "pointerover":
hover(true);
break;
case "mouseout":
case "MSPointerOut":
case "pointerout":
hover(false);
active(false);
break;
case "mousedown":
case "touchstart":
case "MSPointerDown":
case "pointerdown":
case "keydown":
active(true);
break;
case "mouseup":
case "MSPointerUp":
case "pointerup":
case "dojotouchend":
case "keyup":
active(false);
break;
case "focus":
case "focusin":
focused(true);
break;
case "blur":
case "focusout":
focused(false);
break;
}
},
_trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
// summary:
// Track mouse/focus events on specified node and set CSS class on that node to indicate
// current state. Usually not called directly, but via cssStateNodes attribute.
// description:
// Given class=foo, will set the following CSS class on the node
//
// - fooActive: if the user is currently pressing down the mouse button while over the node
// - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
// - fooFocus: if the node is focused
//
// Note that it won't set any classes if the widget is disabled.
// node: DomNode
// Should be a sub-node of the widget, not the top node (this.domNode), since the top node
// is handled specially and automatically just by mixing in this class.
// clazz: String
// CSS class name (ex: dijitSliderUpArrow)
// Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
// when node is hovered/active
node._cssState = clazz;
}
});
domReady(function(){
// Document level listener to catch hover etc. events on widget root nodes and subnodes.
// Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
// have been hovered or unhovered (try test_Accordion.html)
function pointerHandler(evt, target, relatedTarget){
// Handler for mouseover, mouseout, a11yclick.press and a11click.release events
// Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
// to avoid processing mouseout events moving from a widget's domNode to a descendant node;
// such events shouldn't be interpreted as a mouseleave on the widget.
if(relatedTarget && dom.isDescendant(relatedTarget, target)){
return;
}
for(var node = target; node && node != relatedTarget; node = node.parentNode){
// Process any nodes with _cssState property. They are generally widget root nodes,
// but could also be sub-nodes within a widget
if(node._cssState){
var widget = registry.getEnclosingWidget(node);
if(widget){
if(node == widget.domNode){
// event on the widget's root node
widget._cssMouseEvent(evt);
}else{
// event on widget's sub-node
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
}
}
}
}
var body = win.body(), activeNode;
// Handle pointer related events (i.e. mouse or touch)
on(body, touch.over, function(evt){
// Using touch.over rather than mouseover mainly to ignore phantom mouse events on iOS.
pointerHandler(evt, evt.target, evt.relatedTarget);
});
on(body, touch.out, function(evt){
// Using touch.out rather than mouseout mainly to ignore phantom mouse events on iOS.
pointerHandler(evt, evt.target, evt.relatedTarget);
});
on(body, a11yclick.press, function(evt){
// Save the a11yclick.press target to reference when the a11yclick.release comes.
activeNode = evt.target;
pointerHandler(evt, activeNode)
});
on(body, a11yclick.release, function(evt){
// The release event could come on a separate node than the press event, if for example user slid finger.
// Reference activeNode to reset the state of the node that got state set in the a11yclick.press handler.
pointerHandler(evt, activeNode);
activeNode = null;
});
// Track focus events on widget sub-nodes that have been registered via _trackMouseState().
// However, don't track focus events on the widget root nodes, because focus is tracked via the
// focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
// nodes or a subwidget's node or a popup node, etc.)
// Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
on(body, "focusin, focusout", function(evt){
var node = evt.target;
if(node._cssState && !node.getAttribute("widgetId")){
var widget = registry.getEnclosingWidget(node);
if(widget){
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
}
});
});
return CssStateMixin;
});
@@ -1,14 +1,75 @@
//>>built
define("dijit/_DialogMixin",["dojo/_base/declare","./a11y"],function(_1,_2){
return _1("dijit._DialogMixin",null,{actionBarTemplate:"",execute:function(){
},onCancel:function(){
},onExecute:function(){
},_onSubmit:function(){
this.onExecute();
this.execute(this.get("value"));
},_getFocusItems:function(){
var _3=_2._getTabNavigable(this.domNode);
this._firstFocusItem=_3.lowest||_3.first||this.closeButtonNode||this.domNode;
this._lastFocusItem=_3.last||_3.highest||this._firstFocusItem;
}});
define([
"dojo/_base/declare", // declare
"./a11y" // _getTabNavigable
], function(declare, a11y){
// module:
// dijit/_DialogMixin
return declare("dijit._DialogMixin", null, {
// summary:
// This provides functions useful to Dialog and TooltipDialog
// actionBarTemplate: String
// HTML snippet to show the action bar (gray bar with OK/cancel buttons).
// Blank by default, but used by ConfirmDialog/ConfirmTooltipDialog subclasses.
actionBarTemplate: "",
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Callback when the user hits the submit button.
// Override this method to handle Dialog execution.
// description:
// After the user has pressed the submit button, the Dialog
// first calls onExecute() to notify the container to hide the
// dialog and restore focus to wherever it used to be.
//
// *Then* this method is called.
// type:
// callback
},
onCancel: function(){
// summary:
// Called when user has pressed the Dialog's cancel button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
onExecute: function(){
// summary:
// Called when user has pressed the dialog's OK button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
_onSubmit: function(){
// summary:
// Callback when user hits submit button
// type:
// protected
this.onExecute(); // notify container that we are about to execute
this.execute(this.get('value'));
},
_getFocusItems: function(){
// summary:
// Finds focusable items in dialog,
// and sets this._firstFocusItem and this._lastFocusItem
// tags:
// protected
var elems = a11y._getTabNavigable(this.domNode);
this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
}
});
});
@@ -1,75 +0,0 @@
define("dijit/_DialogMixin", [
"dojo/_base/declare", // declare
"./a11y" // _getTabNavigable
], function(declare, a11y){
// module:
// dijit/_DialogMixin
return declare("dijit._DialogMixin", null, {
// summary:
// This provides functions useful to Dialog and TooltipDialog
// actionBarTemplate: String
// HTML snippet to show the action bar (gray bar with OK/cancel buttons).
// Blank by default, but used by ConfirmDialog/ConfirmTooltipDialog subclasses.
actionBarTemplate: "",
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Callback when the user hits the submit button.
// Override this method to handle Dialog execution.
// description:
// After the user has pressed the submit button, the Dialog
// first calls onExecute() to notify the container to hide the
// dialog and restore focus to wherever it used to be.
//
// *Then* this method is called.
// type:
// callback
},
onCancel: function(){
// summary:
// Called when user has pressed the Dialog's cancel button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
onExecute: function(){
// summary:
// Called when user has pressed the dialog's OK button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
_onSubmit: function(){
// summary:
// Callback when user hits submit button
// type:
// protected
this.onExecute(); // notify container that we are about to execute
this.execute(this.get('value'));
},
_getFocusItems: function(){
// summary:
// Finds focusable items in dialog,
// and sets this._firstFocusItem and this._lastFocusItem
// tags:
// protected
var elems = a11y._getTabNavigable(this.domNode);
this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
}
});
});
@@ -1,11 +1,66 @@
//>>built
define("dijit/_FocusMixin",["./focus","./_WidgetBase","dojo/_base/declare","dojo/_base/lang"],function(_1,_2,_3,_4){
_4.extend(_2,{focused:false,onFocus:function(){
},onBlur:function(){
},_onFocus:function(){
this.onFocus();
},_onBlur:function(){
this.onBlur();
}});
return _3("dijit._FocusMixin",null,{_focusManager:_1});
define([
"./focus",
"./_WidgetBase",
"dojo/_base/declare", // declare
"dojo/_base/lang" // lang.extend
], function(focus, _WidgetBase, declare, lang){
// module:
// dijit/_FocusMixin
// We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
// to be last in the inheritance chain, so mixin to _WidgetBase.
lang.extend(_WidgetBase, {
// focused: [readonly] Boolean
// This widget or a widget it contains has focus, or is "active" because
// it was recently clicked.
focused: false,
onFocus: function(){
// summary:
// Called when the widget becomes "active" because
// it or a widget inside of it either has focus, or has recently
// been clicked.
// tags:
// callback
},
onBlur: function(){
// summary:
// Called when the widget stops being "active" because
// focus moved to something outside of it, or the user
// clicked somewhere outside of it, or the widget was
// hidden.
// tags:
// callback
},
_onFocus: function(){
// summary:
// This is where widgets do processing for when they are active,
// such as changing CSS classes. See onFocus() for more details.
// tags:
// protected
this.onFocus();
},
_onBlur: function(){
// summary:
// This is where widgets do processing for when they stop being active,
// such as changing CSS classes. See onBlur() for more details.
// tags:
// protected
this.onBlur();
}
});
return declare("dijit._FocusMixin", null, {
// summary:
// Mixin to widget to provide _onFocus() and _onBlur() methods that
// fire when a widget or its descendants get/lose focus
// flag that I want _onFocus()/_onBlur() notifications from focus manager
_focusManager: focus
});
});
@@ -1,66 +0,0 @@
define("dijit/_FocusMixin", [
"./focus",
"./_WidgetBase",
"dojo/_base/declare", // declare
"dojo/_base/lang" // lang.extend
], function(focus, _WidgetBase, declare, lang){
// module:
// dijit/_FocusMixin
// We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
// to be last in the inheritance chain, so mixin to _WidgetBase.
lang.extend(_WidgetBase, {
// focused: [readonly] Boolean
// This widget or a widget it contains has focus, or is "active" because
// it was recently clicked.
focused: false,
onFocus: function(){
// summary:
// Called when the widget becomes "active" because
// it or a widget inside of it either has focus, or has recently
// been clicked.
// tags:
// callback
},
onBlur: function(){
// summary:
// Called when the widget stops being "active" because
// focus moved to something outside of it, or the user
// clicked somewhere outside of it, or the widget was
// hidden.
// tags:
// callback
},
_onFocus: function(){
// summary:
// This is where widgets do processing for when they are active,
// such as changing CSS classes. See onFocus() for more details.
// tags:
// protected
this.onFocus();
},
_onBlur: function(){
// summary:
// This is where widgets do processing for when they stop being active,
// such as changing CSS classes. See onBlur() for more details.
// tags:
// protected
this.onBlur();
}
});
return declare("dijit._FocusMixin", null, {
// summary:
// Mixin to widget to provide _onFocus() and _onBlur() methods that
// fire when a widget or its descendants get/lose focus
// flag that I want _onFocus()/_onBlur() notifications from focus manager
_focusManager: focus
});
});
@@ -1,183 +1,467 @@
//>>built
define("dijit/_HasDropDown",["dojo/_base/declare","dojo/_base/Deferred","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/dom-geometry","dojo/dom-style","dojo/has","dojo/keys","dojo/_base/lang","dojo/on","dojo/touch","./registry","./focus","./popup","./_FocusMixin"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,on,_b,_c,_d,_e,_f){
return _1("dijit._HasDropDown",_f,{_buttonNode:null,_arrowWrapperNode:null,_popupStateNode:null,_aroundNode:null,dropDown:null,autoWidth:true,forceWidth:false,maxHeight:-1,dropDownPosition:["below","above"],_stopClickEvents:true,_onDropDownMouseDown:function(e){
if(this.disabled||this.readOnly){
return;
}
if(e.type!="MSPointerDown"&&e.type!="pointerdown"){
e.preventDefault();
}
this.own(on.once(this.ownerDocument,_b.release,_a.hitch(this,"_onDropDownMouseUp")));
this.toggleDropDown();
},_onDropDownMouseUp:function(e){
var _10=this.dropDown,_11=false;
if(e&&this._opened){
var c=_6.position(this._buttonNode,true);
if(!(e.pageX>=c.x&&e.pageX<=c.x+c.w)||!(e.pageY>=c.y&&e.pageY<=c.y+c.h)){
var t=e.target;
while(t&&!_11){
if(_5.contains(t,"dijitPopup")){
_11=true;
}else{
t=t.parentNode;
}
}
if(_11){
t=e.target;
if(_10.onItemClick){
var _12;
while(t&&!(_12=_c.byNode(t))){
t=t.parentNode;
}
if(_12&&_12.onClick&&_12.getParent){
_12.getParent().onItemClick(_12,e);
}
}
return;
}
}
}
if(this._opened){
if(_10.focus&&(_10.autoFocus!==false||(e.type=="mouseup"&&!this.hovering))){
this._focusDropDownTimer=this.defer(function(){
_10.focus();
delete this._focusDropDownTimer;
});
}
}else{
if(this.focus){
this.defer("focus");
}
}
},_onDropDownClick:function(e){
if(this._stopClickEvents){
e.stopPropagation();
e.preventDefault();
}
},buildRendering:function(){
this.inherited(arguments);
this._buttonNode=this._buttonNode||this.focusNode||this.domNode;
this._popupStateNode=this._popupStateNode||this.focusNode||this._buttonNode;
var _13={"after":this.isLeftToRight()?"Right":"Left","before":this.isLeftToRight()?"Left":"Right","above":"Up","below":"Down","left":"Left","right":"Right"}[this.dropDownPosition[0]]||this.dropDownPosition[0]||"Down";
_5.add(this._arrowWrapperNode||this._buttonNode,"dijit"+_13+"ArrowButton");
},postCreate:function(){
this.inherited(arguments);
var _14=this.focusNode||this.domNode;
this.own(on(this._buttonNode,_b.press,_a.hitch(this,"_onDropDownMouseDown")),on(this._buttonNode,"click",_a.hitch(this,"_onDropDownClick")),on(_14,"keydown",_a.hitch(this,"_onKey")),on(_14,"keyup",_a.hitch(this,"_onKeyUp")));
},destroy:function(){
if(this._opened){
this.closeDropDown(true);
}
if(this.dropDown){
if(!this.dropDown._destroyed){
this.dropDown.destroyRecursive();
}
delete this.dropDown;
}
this.inherited(arguments);
},_onKey:function(e){
if(this.disabled||this.readOnly){
return;
}
var d=this.dropDown,_15=e.target;
if(d&&this._opened&&d.handleKey){
if(d.handleKey(e)===false){
e.stopPropagation();
e.preventDefault();
return;
}
}
if(d&&this._opened&&e.keyCode==_9.ESCAPE){
this.closeDropDown();
e.stopPropagation();
e.preventDefault();
}else{
if(!this._opened&&(e.keyCode==_9.DOWN_ARROW||((e.keyCode==_9.ENTER||(e.keyCode==_9.SPACE&&(!this._searchTimer||(e.ctrlKey||e.altKey||e.metaKey))))&&((_15.tagName||"").toLowerCase()!=="input"||(_15.type&&_15.type.toLowerCase()!=="text"))))){
this._toggleOnKeyUp=true;
e.stopPropagation();
e.preventDefault();
}
}
},_onKeyUp:function(){
if(this._toggleOnKeyUp){
delete this._toggleOnKeyUp;
this.toggleDropDown();
var d=this.dropDown;
if(d&&d.focus){
this.defer(_a.hitch(d,"focus"),1);
}
}
},_onBlur:function(){
this.closeDropDown(false);
this.inherited(arguments);
},isLoaded:function(){
return true;
},loadDropDown:function(_16){
_16();
},loadAndOpenDropDown:function(){
var d=new _2(),_17=_a.hitch(this,function(){
this.openDropDown();
d.resolve(this.dropDown);
});
if(!this.isLoaded()){
this.loadDropDown(_17);
}else{
_17();
}
return d;
},toggleDropDown:function(){
if(this.disabled||this.readOnly){
return;
}
if(!this._opened){
this.loadAndOpenDropDown();
}else{
this.closeDropDown(true);
}
},openDropDown:function(){
var _18=this.dropDown,_19=_18.domNode,_1a=this._aroundNode||this.domNode,_1b=this;
var _1c=_e.open({parent:this,popup:_18,around:_1a,orient:this.dropDownPosition,maxHeight:this.maxHeight,onExecute:function(){
_1b.closeDropDown(true);
},onCancel:function(){
_1b.closeDropDown(true);
},onClose:function(){
_4.set(_1b._popupStateNode,"popupActive",false);
_5.remove(_1b._popupStateNode,"dijitHasDropDownOpen");
_1b._set("_opened",false);
}});
if(this.forceWidth||(this.autoWidth&&_1a.offsetWidth>_18._popupWrapper.offsetWidth)){
var _1d=_1a.offsetWidth-_18._popupWrapper.offsetWidth;
var _1e={w:_18.domNode.offsetWidth+_1d};
if(_a.isFunction(_18.resize)){
_18.resize(_1e);
}else{
_6.setMarginBox(_19,_1e);
}
if(_1c.corner[1]=="R"){
_18._popupWrapper.style.left=(_18._popupWrapper.style.left.replace("px","")-_1d)+"px";
}
}
_4.set(this._popupStateNode,"popupActive","true");
_5.add(this._popupStateNode,"dijitHasDropDownOpen");
this._set("_opened",true);
this._popupStateNode.setAttribute("aria-expanded","true");
this._popupStateNode.setAttribute("aria-owns",_18.id);
if(_19.getAttribute("role")!=="presentation"&&!_19.getAttribute("aria-labelledby")){
_19.setAttribute("aria-labelledby",this.id);
}
return _1c;
},closeDropDown:function(_1f){
if(this._focusDropDownTimer){
this._focusDropDownTimer.remove();
delete this._focusDropDownTimer;
}
if(this._opened){
this._popupStateNode.setAttribute("aria-expanded","false");
if(_1f&&this.focus){
this.focus();
}
_e.close(this.dropDown);
this._opened=false;
}
}});
define([
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/dom", // dom.isDescendant
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.contains domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/has", // has("touch")
"dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.hitch lang.isFunction
"dojo/on",
"dojo/touch",
"./registry", // registry.byNode()
"./focus",
"./popup",
"./_FocusMixin"
], function(declare, Deferred, dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, on, touch,
registry, focus, popup, _FocusMixin){
// module:
// dijit/_HasDropDown
return declare("dijit._HasDropDown", _FocusMixin, {
// summary:
// Mixin for widgets that need drop down ability.
// _buttonNode: [protected] DomNode
// The button/icon/node to click to display the drop down.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
_buttonNode: null,
// _arrowWrapperNode: [protected] DomNode
// Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
// on where the drop down is set to be positioned.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then _buttonNode will be used.
_arrowWrapperNode: null,
// _popupStateNode: [protected] DomNode
// The node to set the aria-expanded class on.
// Also sets popupActive class but that will be removed in 2.0.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
_popupStateNode: null,
// _aroundNode: [protected] DomNode
// The node to display the popup around.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then domNode will be used.
_aroundNode: null,
// dropDown: [protected] Widget
// The widget to display as a popup. This widget *must* be
// defined before the startup function is called.
dropDown: null,
// autoWidth: [protected] Boolean
// Set to true to make the drop down at least as wide as this
// widget. Set to false if the drop down should just be its
// default width.
autoWidth: true,
// forceWidth: [protected] Boolean
// Set to true to make the drop down exactly as wide as this
// widget. Overrides autoWidth.
forceWidth: false,
// maxHeight: [protected] Integer
// The max height for our dropdown.
// Any dropdown taller than this will have scrollbars.
// Set to 0 for no max height, or -1 to limit height to available space in viewport
maxHeight: -1,
// dropDownPosition: [const] String[]
// This variable controls the position of the drop down.
// It's an array of strings with the following values:
//
// - before: places drop down to the left of the target node/widget, or to the right in
// the case of RTL scripts like Hebrew and Arabic
// - after: places drop down to the right of the target node/widget, or to the left in
// the case of RTL scripts like Hebrew and Arabic
// - above: drop down goes above target node
// - below: drop down goes below target node
//
// The list is positions is tried, in order, until a position is found where the drop down fits
// within the viewport.
//
dropDownPosition: ["below", "above"],
// _stopClickEvents: Boolean
// When set to false, the click events will not be stopped, in
// case you want to use them in your subclass
_stopClickEvents: true,
_onDropDownMouseDown: function(/*Event*/ e){
// summary:
// Callback when the user mousedown/touchstart on the arrow icon.
if(this.disabled || this.readOnly){
return;
}
// Prevent default to stop things like text selection, but don't stop propagation, so that:
// 1. TimeTextBox etc. can focus the <input> on mousedown
// 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
// 3. user defined onMouseDown handler fires
//
// Also, don't call preventDefault() on MSPointerDown event (on IE10) because that prevents the button
// from getting focus, and then the focus manager doesn't know what's going on (#17262)
if(e.type != "MSPointerDown" && e.type != "pointerdown"){
e.preventDefault();
}
this.own(on.once(this.ownerDocument, touch.release, lang.hitch(this, "_onDropDownMouseUp")));
this.toggleDropDown();
},
_onDropDownMouseUp: function(/*Event?*/ e){
// summary:
// Callback on mouseup/touchend after mousedown/touchstart on the arrow icon.
// Note that this function is called regardless of what node the event occurred on (but only after
// a mousedown/touchstart on the arrow).
//
// If the drop down is a simple menu and the cursor is over the menu, we execute it, otherwise, we focus our
// drop down widget. If the event is missing, then we are not
// a mouseup event.
//
// This is useful for the common mouse movement pattern
// with native browser `<select>` nodes:
//
// 1. mouse down on the select node (probably on the arrow)
// 2. move mouse to a menu item while holding down the mouse button
// 3. mouse up. this selects the menu item as though the user had clicked it.
var dropDown = this.dropDown, overMenu = false;
if(e && this._opened){
// This code deals with the corner-case when the drop down covers the original widget,
// because it's so large. In that case mouse-up shouldn't select a value from the menu.
// Find out if our target is somewhere in our dropdown widget,
// but not over our _buttonNode (the clickable node)
var c = domGeometry.position(this._buttonNode, true);
if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) || !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
var t = e.target;
while(t && !overMenu){
if(domClass.contains(t, "dijitPopup")){
overMenu = true;
}else{
t = t.parentNode;
}
}
if(overMenu){
t = e.target;
if(dropDown.onItemClick){
var menuItem;
while(t && !(menuItem = registry.byNode(t))){
t = t.parentNode;
}
if(menuItem && menuItem.onClick && menuItem.getParent){
menuItem.getParent().onItemClick(menuItem, e);
}
}
return;
}
}
}
if(this._opened){
// Focus the dropdown widget unless it's a menu (in which case autoFocus is set to false).
// Even if it's a menu, we need to focus it if this is a fake mouse event caused by the user typing
// SPACE/ENTER while using JAWS. Jaws converts the SPACE/ENTER key into mousedown/mouseup events.
// If this.hovering is false then it's presumably actually a keyboard event.
if(dropDown.focus && (dropDown.autoFocus !== false || (e.type == "mouseup" && !this.hovering))){
// Do it on a delay so that we don't steal back focus from the dropdown.
this._focusDropDownTimer = this.defer(function(){
dropDown.focus();
delete this._focusDropDownTimer;
});
}
}else{
// The drop down arrow icon probably can't receive focus, but widget itself should get focus.
// defer() needed to make it work on IE (test DateTextBox)
if(this.focus){
this.defer("focus");
}
}
},
_onDropDownClick: function(/*Event*/ e){
// The drop down was already opened on mousedown/keydown; just need to stop the event
if(this._stopClickEvents){
e.stopPropagation();
e.preventDefault();
}
},
buildRendering: function(){
this.inherited(arguments);
this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
// Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
// based on where drop down will normally appear
var defaultPos = {
"after": this.isLeftToRight() ? "Right" : "Left",
"before": this.isLeftToRight() ? "Left" : "Right",
"above": "Up",
"below": "Down",
"left": "Left",
"right": "Right"
}[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
},
postCreate: function(){
// summary:
// set up nodes and connect our mouse and keyboard events
this.inherited(arguments);
var keyboardEventNode = this.focusNode || this.domNode;
this.own(
on(this._buttonNode, touch.press, lang.hitch(this, "_onDropDownMouseDown")),
on(this._buttonNode, "click", lang.hitch(this, "_onDropDownClick")),
on(keyboardEventNode, "keydown", lang.hitch(this, "_onKey")),
on(keyboardEventNode, "keyup", lang.hitch(this, "_onKeyUp"))
);
},
destroy: function(){
// If dropdown is open, close it, to avoid leaving dijit/focus in a strange state.
// Put focus back on me to avoid the focused node getting destroyed, which flummoxes IE.
if(this._opened){
this.closeDropDown(true);
}
if(this.dropDown){
// Destroy the drop down, unless it's already been destroyed. This can happen because
// the drop down is a direct child of <body> even though it's logically my child.
if(!this.dropDown._destroyed){
this.dropDown.destroyRecursive();
}
delete this.dropDown;
}
this.inherited(arguments);
},
_onKey: function(/*Event*/ e){
// summary:
// Callback when the user presses a key while focused on the button node
if(this.disabled || this.readOnly){
return;
}
var d = this.dropDown, target = e.target;
if(d && this._opened && d.handleKey){
if(d.handleKey(e) === false){
/* false return code means that the drop down handled the key */
e.stopPropagation();
e.preventDefault();
return;
}
}
if(d && this._opened && e.keyCode == keys.ESCAPE){
this.closeDropDown();
e.stopPropagation();
e.preventDefault();
}else if(!this._opened &&
(e.keyCode == keys.DOWN_ARROW ||
// ignore unmodified SPACE if _KeyNavMixin has active searching in progress
( (e.keyCode == keys.ENTER || (e.keyCode == keys.SPACE && (!this._searchTimer || (e.ctrlKey || e.altKey || e.metaKey)))) &&
//ignore enter and space if the event is for a text input
((target.tagName || "").toLowerCase() !== 'input' ||
(target.type && target.type.toLowerCase() !== 'text'))))){
// Toggle the drop down, but wait until keyup so that the drop down doesn't
// get a stray keyup event, or in the case of key-repeat (because user held
// down key for too long), stray keydown events
this._toggleOnKeyUp = true;
e.stopPropagation();
e.preventDefault();
}
},
_onKeyUp: function(){
if(this._toggleOnKeyUp){
delete this._toggleOnKeyUp;
this.toggleDropDown();
var d = this.dropDown; // drop down may not exist until toggleDropDown() call
if(d && d.focus){
this.defer(lang.hitch(d, "focus"), 1);
}
}
},
_onBlur: function(){
// summary:
// Called magically when focus has shifted away from this widget and it's dropdown
// Close dropdown but don't focus my <input>. User may have focused somewhere else (ex: clicked another
// input), and even if they just clicked a blank area of the screen, focusing my <input> will unwantedly
// popup the keyboard on mobile.
this.closeDropDown(false);
this.inherited(arguments);
},
isLoaded: function(){
// summary:
// Returns true if the dropdown exists and it's data is loaded. This can
// be overridden in order to force a call to loadDropDown().
// tags:
// protected
return true;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and then calls
// the given callback.
// tags:
// protected
// TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
loadCallback();
},
loadAndOpenDropDown: function(){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and
// then opens the drop down. This is basically a callback when the
// user presses the down arrow button to open the drop down.
// returns: Deferred
// Deferred for the drop down widget that
// fires when drop down is created and loaded
// tags:
// protected
var d = new Deferred(),
afterLoad = lang.hitch(this, function(){
this.openDropDown();
d.resolve(this.dropDown);
});
if(!this.isLoaded()){
this.loadDropDown(afterLoad);
}else{
afterLoad();
}
return d;
},
toggleDropDown: function(){
// summary:
// Callback when the user presses the down arrow button or presses
// the down arrow key to open/close the drop down.
// Toggle the drop-down widget; if it is up, close it, if not, open it
// tags:
// protected
if(this.disabled || this.readOnly){
return;
}
if(!this._opened){
this.loadAndOpenDropDown();
}else{
this.closeDropDown(true); // refocus button to avoid hiding node w/focus
}
},
openDropDown: function(){
// summary:
// Opens the dropdown for this widget. To be called only when this.dropDown
// has been created and is ready to display (ie, it's data is loaded).
// returns:
// return value of dijit/popup.open()
// tags:
// protected
var dropDown = this.dropDown,
ddNode = dropDown.domNode,
aroundNode = this._aroundNode || this.domNode,
self = this;
var retVal = popup.open({
parent: this,
popup: dropDown,
around: aroundNode,
orient: this.dropDownPosition,
maxHeight: this.maxHeight,
onExecute: function(){
self.closeDropDown(true);
},
onCancel: function(){
self.closeDropDown(true);
},
onClose: function(){
domAttr.set(self._popupStateNode, "popupActive", false);
domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
self._set("_opened", false); // use set() because _CssStateMixin is watching
}
});
// Set width of drop down if necessary, so that dropdown width + width of scrollbar (from popup wrapper)
// matches width of aroundNode
if(this.forceWidth || (this.autoWidth && aroundNode.offsetWidth > dropDown._popupWrapper.offsetWidth)){
var widthAdjust = aroundNode.offsetWidth - dropDown._popupWrapper.offsetWidth;
var resizeArgs = {
w: dropDown.domNode.offsetWidth + widthAdjust
};
if(lang.isFunction(dropDown.resize)){
dropDown.resize(resizeArgs);
}else{
domGeometry.setMarginBox(ddNode, resizeArgs);
}
// If dropdown is right-aligned then compensate for width change by changing horizontal position
if(retVal.corner[1] == "R"){
dropDown._popupWrapper.style.left =
(dropDown._popupWrapper.style.left.replace("px", "") - widthAdjust) + "px";
}
}
domAttr.set(this._popupStateNode, "popupActive", "true");
domClass.add(this._popupStateNode, "dijitHasDropDownOpen");
this._set("_opened", true); // use set() because _CssStateMixin is watching
this._popupStateNode.setAttribute("aria-expanded", "true");
this._popupStateNode.setAttribute("aria-owns", dropDown.id);
// Set aria-labelledby on dropdown if it's not already set to something more meaningful
if(ddNode.getAttribute("role") !== "presentation" && !ddNode.getAttribute("aria-labelledby")){
ddNode.setAttribute("aria-labelledby", this.id);
}
return retVal;
},
closeDropDown: function(/*Boolean*/ focus){
// summary:
// Closes the drop down on this widget
// focus:
// If true, refocuses the button widget
// tags:
// protected
if(this._focusDropDownTimer){
this._focusDropDownTimer.remove();
delete this._focusDropDownTimer;
}
if(this._opened){
this._popupStateNode.setAttribute("aria-expanded", "false");
if(focus && this.focus){
this.focus();
}
popup.close(this.dropDown);
this._opened = false;
}
}
});
});
@@ -1,467 +0,0 @@
define("dijit/_HasDropDown", [
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/dom", // dom.isDescendant
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.contains domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/has", // has("touch")
"dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.hitch lang.isFunction
"dojo/on",
"dojo/touch",
"./registry", // registry.byNode()
"./focus",
"./popup",
"./_FocusMixin"
], function(declare, Deferred, dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, on, touch,
registry, focus, popup, _FocusMixin){
// module:
// dijit/_HasDropDown
return declare("dijit._HasDropDown", _FocusMixin, {
// summary:
// Mixin for widgets that need drop down ability.
// _buttonNode: [protected] DomNode
// The button/icon/node to click to display the drop down.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
_buttonNode: null,
// _arrowWrapperNode: [protected] DomNode
// Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
// on where the drop down is set to be positioned.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then _buttonNode will be used.
_arrowWrapperNode: null,
// _popupStateNode: [protected] DomNode
// The node to set the aria-expanded class on.
// Also sets popupActive class but that will be removed in 2.0.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
_popupStateNode: null,
// _aroundNode: [protected] DomNode
// The node to display the popup around.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then domNode will be used.
_aroundNode: null,
// dropDown: [protected] Widget
// The widget to display as a popup. This widget *must* be
// defined before the startup function is called.
dropDown: null,
// autoWidth: [protected] Boolean
// Set to true to make the drop down at least as wide as this
// widget. Set to false if the drop down should just be its
// default width.
autoWidth: true,
// forceWidth: [protected] Boolean
// Set to true to make the drop down exactly as wide as this
// widget. Overrides autoWidth.
forceWidth: false,
// maxHeight: [protected] Integer
// The max height for our dropdown.
// Any dropdown taller than this will have scrollbars.
// Set to 0 for no max height, or -1 to limit height to available space in viewport
maxHeight: -1,
// dropDownPosition: [const] String[]
// This variable controls the position of the drop down.
// It's an array of strings with the following values:
//
// - before: places drop down to the left of the target node/widget, or to the right in
// the case of RTL scripts like Hebrew and Arabic
// - after: places drop down to the right of the target node/widget, or to the left in
// the case of RTL scripts like Hebrew and Arabic
// - above: drop down goes above target node
// - below: drop down goes below target node
//
// The list is positions is tried, in order, until a position is found where the drop down fits
// within the viewport.
//
dropDownPosition: ["below", "above"],
// _stopClickEvents: Boolean
// When set to false, the click events will not be stopped, in
// case you want to use them in your subclass
_stopClickEvents: true,
_onDropDownMouseDown: function(/*Event*/ e){
// summary:
// Callback when the user mousedown/touchstart on the arrow icon.
if(this.disabled || this.readOnly){
return;
}
// Prevent default to stop things like text selection, but don't stop propagation, so that:
// 1. TimeTextBox etc. can focus the <input> on mousedown
// 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
// 3. user defined onMouseDown handler fires
//
// Also, don't call preventDefault() on MSPointerDown event (on IE10) because that prevents the button
// from getting focus, and then the focus manager doesn't know what's going on (#17262)
if(e.type != "MSPointerDown" && e.type != "pointerdown"){
e.preventDefault();
}
this.own(on.once(this.ownerDocument, touch.release, lang.hitch(this, "_onDropDownMouseUp")));
this.toggleDropDown();
},
_onDropDownMouseUp: function(/*Event?*/ e){
// summary:
// Callback on mouseup/touchend after mousedown/touchstart on the arrow icon.
// Note that this function is called regardless of what node the event occurred on (but only after
// a mousedown/touchstart on the arrow).
//
// If the drop down is a simple menu and the cursor is over the menu, we execute it, otherwise, we focus our
// drop down widget. If the event is missing, then we are not
// a mouseup event.
//
// This is useful for the common mouse movement pattern
// with native browser `<select>` nodes:
//
// 1. mouse down on the select node (probably on the arrow)
// 2. move mouse to a menu item while holding down the mouse button
// 3. mouse up. this selects the menu item as though the user had clicked it.
var dropDown = this.dropDown, overMenu = false;
if(e && this._opened){
// This code deals with the corner-case when the drop down covers the original widget,
// because it's so large. In that case mouse-up shouldn't select a value from the menu.
// Find out if our target is somewhere in our dropdown widget,
// but not over our _buttonNode (the clickable node)
var c = domGeometry.position(this._buttonNode, true);
if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) || !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
var t = e.target;
while(t && !overMenu){
if(domClass.contains(t, "dijitPopup")){
overMenu = true;
}else{
t = t.parentNode;
}
}
if(overMenu){
t = e.target;
if(dropDown.onItemClick){
var menuItem;
while(t && !(menuItem = registry.byNode(t))){
t = t.parentNode;
}
if(menuItem && menuItem.onClick && menuItem.getParent){
menuItem.getParent().onItemClick(menuItem, e);
}
}
return;
}
}
}
if(this._opened){
// Focus the dropdown widget unless it's a menu (in which case autoFocus is set to false).
// Even if it's a menu, we need to focus it if this is a fake mouse event caused by the user typing
// SPACE/ENTER while using JAWS. Jaws converts the SPACE/ENTER key into mousedown/mouseup events.
// If this.hovering is false then it's presumably actually a keyboard event.
if(dropDown.focus && (dropDown.autoFocus !== false || (e.type == "mouseup" && !this.hovering))){
// Do it on a delay so that we don't steal back focus from the dropdown.
this._focusDropDownTimer = this.defer(function(){
dropDown.focus();
delete this._focusDropDownTimer;
});
}
}else{
// The drop down arrow icon probably can't receive focus, but widget itself should get focus.
// defer() needed to make it work on IE (test DateTextBox)
if(this.focus){
this.defer("focus");
}
}
},
_onDropDownClick: function(/*Event*/ e){
// The drop down was already opened on mousedown/keydown; just need to stop the event
if(this._stopClickEvents){
e.stopPropagation();
e.preventDefault();
}
},
buildRendering: function(){
this.inherited(arguments);
this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
// Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
// based on where drop down will normally appear
var defaultPos = {
"after": this.isLeftToRight() ? "Right" : "Left",
"before": this.isLeftToRight() ? "Left" : "Right",
"above": "Up",
"below": "Down",
"left": "Left",
"right": "Right"
}[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
},
postCreate: function(){
// summary:
// set up nodes and connect our mouse and keyboard events
this.inherited(arguments);
var keyboardEventNode = this.focusNode || this.domNode;
this.own(
on(this._buttonNode, touch.press, lang.hitch(this, "_onDropDownMouseDown")),
on(this._buttonNode, "click", lang.hitch(this, "_onDropDownClick")),
on(keyboardEventNode, "keydown", lang.hitch(this, "_onKey")),
on(keyboardEventNode, "keyup", lang.hitch(this, "_onKeyUp"))
);
},
destroy: function(){
// If dropdown is open, close it, to avoid leaving dijit/focus in a strange state.
// Put focus back on me to avoid the focused node getting destroyed, which flummoxes IE.
if(this._opened){
this.closeDropDown(true);
}
if(this.dropDown){
// Destroy the drop down, unless it's already been destroyed. This can happen because
// the drop down is a direct child of <body> even though it's logically my child.
if(!this.dropDown._destroyed){
this.dropDown.destroyRecursive();
}
delete this.dropDown;
}
this.inherited(arguments);
},
_onKey: function(/*Event*/ e){
// summary:
// Callback when the user presses a key while focused on the button node
if(this.disabled || this.readOnly){
return;
}
var d = this.dropDown, target = e.target;
if(d && this._opened && d.handleKey){
if(d.handleKey(e) === false){
/* false return code means that the drop down handled the key */
e.stopPropagation();
e.preventDefault();
return;
}
}
if(d && this._opened && e.keyCode == keys.ESCAPE){
this.closeDropDown();
e.stopPropagation();
e.preventDefault();
}else if(!this._opened &&
(e.keyCode == keys.DOWN_ARROW ||
// ignore unmodified SPACE if _KeyNavMixin has active searching in progress
( (e.keyCode == keys.ENTER || (e.keyCode == keys.SPACE && (!this._searchTimer || (e.ctrlKey || e.altKey || e.metaKey)))) &&
//ignore enter and space if the event is for a text input
((target.tagName || "").toLowerCase() !== 'input' ||
(target.type && target.type.toLowerCase() !== 'text'))))){
// Toggle the drop down, but wait until keyup so that the drop down doesn't
// get a stray keyup event, or in the case of key-repeat (because user held
// down key for too long), stray keydown events
this._toggleOnKeyUp = true;
e.stopPropagation();
e.preventDefault();
}
},
_onKeyUp: function(){
if(this._toggleOnKeyUp){
delete this._toggleOnKeyUp;
this.toggleDropDown();
var d = this.dropDown; // drop down may not exist until toggleDropDown() call
if(d && d.focus){
this.defer(lang.hitch(d, "focus"), 1);
}
}
},
_onBlur: function(){
// summary:
// Called magically when focus has shifted away from this widget and it's dropdown
// Close dropdown but don't focus my <input>. User may have focused somewhere else (ex: clicked another
// input), and even if they just clicked a blank area of the screen, focusing my <input> will unwantedly
// popup the keyboard on mobile.
this.closeDropDown(false);
this.inherited(arguments);
},
isLoaded: function(){
// summary:
// Returns true if the dropdown exists and it's data is loaded. This can
// be overridden in order to force a call to loadDropDown().
// tags:
// protected
return true;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and then calls
// the given callback.
// tags:
// protected
// TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
loadCallback();
},
loadAndOpenDropDown: function(){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and
// then opens the drop down. This is basically a callback when the
// user presses the down arrow button to open the drop down.
// returns: Deferred
// Deferred for the drop down widget that
// fires when drop down is created and loaded
// tags:
// protected
var d = new Deferred(),
afterLoad = lang.hitch(this, function(){
this.openDropDown();
d.resolve(this.dropDown);
});
if(!this.isLoaded()){
this.loadDropDown(afterLoad);
}else{
afterLoad();
}
return d;
},
toggleDropDown: function(){
// summary:
// Callback when the user presses the down arrow button or presses
// the down arrow key to open/close the drop down.
// Toggle the drop-down widget; if it is up, close it, if not, open it
// tags:
// protected
if(this.disabled || this.readOnly){
return;
}
if(!this._opened){
this.loadAndOpenDropDown();
}else{
this.closeDropDown(true); // refocus button to avoid hiding node w/focus
}
},
openDropDown: function(){
// summary:
// Opens the dropdown for this widget. To be called only when this.dropDown
// has been created and is ready to display (ie, it's data is loaded).
// returns:
// return value of dijit/popup.open()
// tags:
// protected
var dropDown = this.dropDown,
ddNode = dropDown.domNode,
aroundNode = this._aroundNode || this.domNode,
self = this;
var retVal = popup.open({
parent: this,
popup: dropDown,
around: aroundNode,
orient: this.dropDownPosition,
maxHeight: this.maxHeight,
onExecute: function(){
self.closeDropDown(true);
},
onCancel: function(){
self.closeDropDown(true);
},
onClose: function(){
domAttr.set(self._popupStateNode, "popupActive", false);
domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
self._set("_opened", false); // use set() because _CssStateMixin is watching
}
});
// Set width of drop down if necessary, so that dropdown width + width of scrollbar (from popup wrapper)
// matches width of aroundNode
if(this.forceWidth || (this.autoWidth && aroundNode.offsetWidth > dropDown._popupWrapper.offsetWidth)){
var widthAdjust = aroundNode.offsetWidth - dropDown._popupWrapper.offsetWidth;
var resizeArgs = {
w: dropDown.domNode.offsetWidth + widthAdjust
};
if(lang.isFunction(dropDown.resize)){
dropDown.resize(resizeArgs);
}else{
domGeometry.setMarginBox(ddNode, resizeArgs);
}
// If dropdown is right-aligned then compensate for width change by changing horizontal position
if(retVal.corner[1] == "R"){
dropDown._popupWrapper.style.left =
(dropDown._popupWrapper.style.left.replace("px", "") - widthAdjust) + "px";
}
}
domAttr.set(this._popupStateNode, "popupActive", "true");
domClass.add(this._popupStateNode, "dijitHasDropDownOpen");
this._set("_opened", true); // use set() because _CssStateMixin is watching
this._popupStateNode.setAttribute("aria-expanded", "true");
this._popupStateNode.setAttribute("aria-owns", dropDown.id);
// Set aria-labelledby on dropdown if it's not already set to something more meaningful
if(ddNode.getAttribute("role") !== "presentation" && !ddNode.getAttribute("aria-labelledby")){
ddNode.setAttribute("aria-labelledby", this.id);
}
return retVal;
},
closeDropDown: function(/*Boolean*/ focus){
// summary:
// Closes the drop down on this widget
// focus:
// If true, refocuses the button widget
// tags:
// protected
if(this._focusDropDownTimer){
this._focusDropDownTimer.remove();
delete this._focusDropDownTimer;
}
if(this._opened){
this._popupStateNode.setAttribute("aria-expanded", "false");
if(focus && this.focus){
this.focus();
}
popup.close(this.dropDown);
this._opened = false;
}
}
});
});
@@ -1,39 +1,128 @@
//>>built
define("dijit/_KeyNavContainer",["dojo/_base/array","dojo/_base/declare","dojo/dom-attr","dojo/_base/kernel","dojo/keys","dojo/_base/lang","./registry","./_Container","./_FocusMixin","./_KeyNavMixin"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a){
return _2("dijit._KeyNavContainer",[_9,_a,_8],{connectKeyNavHandlers:function(_b,_c){
var _d=(this._keyNavCodes={});
var _e=_6.hitch(this,"focusPrev");
var _f=_6.hitch(this,"focusNext");
_1.forEach(_b,function(_10){
_d[_10]=_e;
});
_1.forEach(_c,function(_11){
_d[_11]=_f;
});
_d[_5.HOME]=_6.hitch(this,"focusFirstChild");
_d[_5.END]=_6.hitch(this,"focusLastChild");
},startupKeyNavChildren:function(){
_4.deprecated("startupKeyNavChildren() call no longer needed","","2.0");
},startup:function(){
this.inherited(arguments);
_1.forEach(this.getChildren(),_6.hitch(this,"_startupChild"));
},addChild:function(_12,_13){
this.inherited(arguments);
this._startupChild(_12);
},_startupChild:function(_14){
_14.set("tabIndex","-1");
},_getFirst:function(){
var _15=this.getChildren();
return _15.length?_15[0]:null;
},_getLast:function(){
var _16=this.getChildren();
return _16.length?_16[_16.length-1]:null;
},focusNext:function(){
this.focusChild(this._getNextFocusableChild(this.focusedChild,1));
},focusPrev:function(){
this.focusChild(this._getNextFocusableChild(this.focusedChild,-1),true);
},childSelector:function(_17){
var _17=_7.byNode(_17);
return _17&&_17.getParent()==this;
}});
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.END keys.HOME
"dojo/_base/lang", // lang.hitch
"./registry",
"./_Container",
"./_FocusMixin",
"./_KeyNavMixin"
], function(array, declare, domAttr, kernel, keys, lang, registry, _Container, _FocusMixin, _KeyNavMixin){
// module:
// dijit/_KeyNavContainer
return declare("dijit._KeyNavContainer", [_FocusMixin, _KeyNavMixin, _Container], {
// summary:
// A _Container with keyboard navigation of its children.
// description:
// Provides normalized keyboard and focusing code for Container widgets.
// To use this mixin, call connectKeyNavHandlers() in postCreate().
// Also, child widgets must implement a focus() method.
connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
// summary:
// Deprecated. You can call this in postCreate() to attach the keyboard handlers to the container,
// but the preferred method is to override _onLeftArrow() and _onRightArrow(), or
// _onUpArrow() and _onDownArrow(), to call focusPrev() and focusNext().
// prevKeyCodes: keys[]
// Key codes for navigating to the previous child.
// nextKeyCodes: keys[]
// Key codes for navigating to the next child.
// tags:
// protected
// TODO: remove for 2.0, and make subclasses override _onLeftArrow, _onRightArrow etc. instead.
var keyCodes = (this._keyNavCodes = {});
var prev = lang.hitch(this, "focusPrev");
var next = lang.hitch(this, "focusNext");
array.forEach(prevKeyCodes, function(code){
keyCodes[code] = prev;
});
array.forEach(nextKeyCodes, function(code){
keyCodes[code] = next;
});
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
},
startupKeyNavChildren: function(){
kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
},
startup: function(){
this.inherited(arguments);
array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
},
addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
this.inherited(arguments);
this._startupChild(widget);
},
_startupChild: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Setup for each child widget.
// description:
// Sets tabIndex=-1 on each child, so that the tab key will
// leave the container rather than visiting each child.
//
// Note: if you add children by a different method than addChild(), then need to call this manually
// or at least make sure the child's tabIndex is -1.
//
// Note: see also _LayoutWidget.setupChild(), which is also called for each child widget.
// tags:
// private
widget.set("tabIndex", "-1");
},
_getFirst: function(){
// summary:
// Returns the first child.
// tags:
// abstract extension
var children = this.getChildren();
return children.length ? children[0] : null;
},
_getLast: function(){
// summary:
// Returns the last descendant.
// tags:
// abstract extension
var children = this.getChildren();
return children.length ? children[children.length - 1] : null;
},
focusNext: function(){
// summary:
// Focus the next widget
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
},
focusPrev: function(){
// summary:
// Focus the last focusable node in the previous widget
// (ex: go to the ComboButton icon section rather than button section)
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
},
childSelector: function(/*DOMNode*/ node){
// Implement _KeyNavMixin.childSelector, to identify focusable child nodes.
// If we allowed a dojo/query dependency from this module this could more simply be a string "> *"
// instead of this function.
var node = registry.byNode(node);
return node && node.getParent() == this;
}
});
});
@@ -1,128 +0,0 @@
define("dijit/_KeyNavContainer", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.END keys.HOME
"dojo/_base/lang", // lang.hitch
"./registry",
"./_Container",
"./_FocusMixin",
"./_KeyNavMixin"
], function(array, declare, domAttr, kernel, keys, lang, registry, _Container, _FocusMixin, _KeyNavMixin){
// module:
// dijit/_KeyNavContainer
return declare("dijit._KeyNavContainer", [_FocusMixin, _KeyNavMixin, _Container], {
// summary:
// A _Container with keyboard navigation of its children.
// description:
// Provides normalized keyboard and focusing code for Container widgets.
// To use this mixin, call connectKeyNavHandlers() in postCreate().
// Also, child widgets must implement a focus() method.
connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
// summary:
// Deprecated. You can call this in postCreate() to attach the keyboard handlers to the container,
// but the preferred method is to override _onLeftArrow() and _onRightArrow(), or
// _onUpArrow() and _onDownArrow(), to call focusPrev() and focusNext().
// prevKeyCodes: keys[]
// Key codes for navigating to the previous child.
// nextKeyCodes: keys[]
// Key codes for navigating to the next child.
// tags:
// protected
// TODO: remove for 2.0, and make subclasses override _onLeftArrow, _onRightArrow etc. instead.
var keyCodes = (this._keyNavCodes = {});
var prev = lang.hitch(this, "focusPrev");
var next = lang.hitch(this, "focusNext");
array.forEach(prevKeyCodes, function(code){
keyCodes[code] = prev;
});
array.forEach(nextKeyCodes, function(code){
keyCodes[code] = next;
});
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
},
startupKeyNavChildren: function(){
kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
},
startup: function(){
this.inherited(arguments);
array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
},
addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
this.inherited(arguments);
this._startupChild(widget);
},
_startupChild: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Setup for each child widget.
// description:
// Sets tabIndex=-1 on each child, so that the tab key will
// leave the container rather than visiting each child.
//
// Note: if you add children by a different method than addChild(), then need to call this manually
// or at least make sure the child's tabIndex is -1.
//
// Note: see also _LayoutWidget.setupChild(), which is also called for each child widget.
// tags:
// private
widget.set("tabIndex", "-1");
},
_getFirst: function(){
// summary:
// Returns the first child.
// tags:
// abstract extension
var children = this.getChildren();
return children.length ? children[0] : null;
},
_getLast: function(){
// summary:
// Returns the last descendant.
// tags:
// abstract extension
var children = this.getChildren();
return children.length ? children[children.length - 1] : null;
},
focusNext: function(){
// summary:
// Focus the next widget
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
},
focusPrev: function(){
// summary:
// Focus the last focusable node in the previous widget
// (ex: go to the ComboButton icon section rather than button section)
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
},
childSelector: function(/*DOMNode*/ node){
// Implement _KeyNavMixin.childSelector, to identify focusable child nodes.
// If we allowed a dojo/query dependency from this module this could more simply be a string "> *"
// instead of this function.
var node = registry.byNode(node);
return node && node.getParent() == this;
}
});
});
@@ -1,166 +1,487 @@
//>>built
define("dijit/_KeyNavMixin",["dojo/_base/array","dojo/_base/declare","dojo/dom-attr","dojo/keys","dojo/_base/lang","dojo/on","dijit/registry","dijit/_FocusMixin"],function(_1,_2,_3,_4,_5,on,_6,_7){
return _2("dijit._KeyNavMixin",_7,{tabIndex:"0",childSelector:null,postCreate:function(){
this.inherited(arguments);
_3.set(this.domNode,"tabIndex",this.tabIndex);
if(!this._keyNavCodes){
var _8=this._keyNavCodes={};
_8[_4.HOME]=_5.hitch(this,"focusFirstChild");
_8[_4.END]=_5.hitch(this,"focusLastChild");
_8[this.isLeftToRight()?_4.LEFT_ARROW:_4.RIGHT_ARROW]=_5.hitch(this,"_onLeftArrow");
_8[this.isLeftToRight()?_4.RIGHT_ARROW:_4.LEFT_ARROW]=_5.hitch(this,"_onRightArrow");
_8[_4.UP_ARROW]=_5.hitch(this,"_onUpArrow");
_8[_4.DOWN_ARROW]=_5.hitch(this,"_onDownArrow");
}
var _9=this,_a=typeof this.childSelector=="string"?this.childSelector:_5.hitch(this,"childSelector");
this.own(on(this.domNode,"keypress",_5.hitch(this,"_onContainerKeypress")),on(this.domNode,"keydown",_5.hitch(this,"_onContainerKeydown")),on(this.domNode,"focus",_5.hitch(this,"_onContainerFocus")),on(this.containerNode,on.selector(_a,"focusin"),function(_b){
_9._onChildFocus(_6.getEnclosingWidget(this),_b);
}));
},_onLeftArrow:function(){
},_onRightArrow:function(){
},_onUpArrow:function(){
},_onDownArrow:function(){
},focus:function(){
this.focusFirstChild();
},_getFirstFocusableChild:function(){
return this._getNextFocusableChild(null,1);
},_getLastFocusableChild:function(){
return this._getNextFocusableChild(null,-1);
},focusFirstChild:function(){
this.focusChild(this._getFirstFocusableChild());
},focusLastChild:function(){
this.focusChild(this._getLastFocusableChild());
},focusChild:function(_c,_d){
if(!_c){
return;
}
if(this.focusedChild&&_c!==this.focusedChild){
this._onChildBlur(this.focusedChild);
}
_c.set("tabIndex",this.tabIndex);
_c.focus(_d?"end":"start");
},_onContainerFocus:function(_e){
if(_e.target!==this.domNode||this.focusedChild){
return;
}
this.focus();
},_onFocus:function(){
_3.set(this.domNode,"tabIndex","-1");
this.inherited(arguments);
},_onBlur:function(_f){
_3.set(this.domNode,"tabIndex",this.tabIndex);
if(this.focusedChild){
this.focusedChild.set("tabIndex","-1");
this.lastFocusedChild=this.focusedChild;
this._set("focusedChild",null);
}
this.inherited(arguments);
},_onChildFocus:function(_10){
if(_10&&_10!=this.focusedChild){
if(this.focusedChild&&!this.focusedChild._destroyed){
this.focusedChild.set("tabIndex","-1");
}
_10.set("tabIndex",this.tabIndex);
this.lastFocused=_10;
this._set("focusedChild",_10);
}
},_searchString:"",multiCharSearchDuration:1000,onKeyboardSearch:function(_11,evt,_12,_13){
if(_11){
this.focusChild(_11);
}
},_keyboardSearchCompare:function(_14,_15){
var _16=_14.domNode,_17=_14.label||(_16.focusNode?_16.focusNode.label:"")||_16.innerText||_16.textContent||"",_18=_17.replace(/^\s+/,"").substr(0,_15.length).toLowerCase();
return (!!_15.length&&_18==_15)?-1:0;
},_onContainerKeydown:function(evt){
var _19=this._keyNavCodes[evt.keyCode];
if(_19){
_19(evt,this.focusedChild);
evt.stopPropagation();
evt.preventDefault();
this._searchString="";
}else{
if(evt.keyCode==_4.SPACE&&this._searchTimer&&!(evt.ctrlKey||evt.altKey||evt.metaKey)){
evt.stopImmediatePropagation();
evt.preventDefault();
this._keyboardSearch(evt," ");
}
}
},_onContainerKeypress:function(evt){
if(evt.charCode<=_4.SPACE||evt.ctrlKey||evt.altKey||evt.metaKey){
return;
}
evt.preventDefault();
evt.stopPropagation();
this._keyboardSearch(evt,String.fromCharCode(evt.charCode).toLowerCase());
},_keyboardSearch:function(evt,_1a){
var _1b=null,_1c,_1d=0,_1e=_5.hitch(this,function(){
if(this._searchTimer){
this._searchTimer.remove();
}
this._searchString+=_1a;
var _1f=/^(.)\1*$/.test(this._searchString);
var _20=_1f?1:this._searchString.length;
_1c=this._searchString.substr(0,_20);
this._searchTimer=this.defer(function(){
this._searchTimer=null;
this._searchString="";
},this.multiCharSearchDuration);
var _21=this.focusedChild||null;
if(_20==1||!_21){
_21=this._getNextFocusableChild(_21,1);
if(!_21){
return;
}
}
var _22=_21;
do{
var rc=this._keyboardSearchCompare(_21,_1c);
if(!!rc&&_1d++==0){
_1b=_21;
}
if(rc==-1){
_1d=-1;
break;
}
_21=this._getNextFocusableChild(_21,1);
}while(_21!=_22);
});
_1e();
this.onKeyboardSearch(_1b,evt,_1c,_1d);
},_onChildBlur:function(){
},_getNextFocusableChild:function(_23,dir){
var _24=_23;
do{
if(!_23){
_23=this[dir>0?"_getFirst":"_getLast"]();
if(!_23){
break;
}
}else{
_23=this._getNext(_23,dir);
}
if(_23!=null&&_23!=_24&&_23.isFocusable()){
return _23;
}
}while(_23!=_24);
return null;
},_getFirst:function(){
return null;
},_getLast:function(){
return null;
},_getNext:function(_25,dir){
if(_25){
_25=_25.domNode;
while(_25){
_25=_25[dir<0?"previousSibling":"nextSibling"];
if(_25&&"getAttribute" in _25){
var w=_6.byNode(_25);
if(w){
return w;
}
}
}
}
return null;
}});
define([
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/keys", // keys.END keys.HOME, keys.LEFT_ARROW etc.
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dijit/registry",
"dijit/_FocusMixin" // to make _onBlur() work
], function(array, declare, domAttr, keys, lang, on, registry, _FocusMixin){
// module:
// dijit/_KeyNavMixin
return declare("dijit._KeyNavMixin", _FocusMixin, {
// summary:
// A mixin to allow arrow key and letter key navigation of child or descendant widgets.
// It can be used by dijit/_Container based widgets with a flat list of children,
// or more complex widgets like dijit/Tree.
//
// To use this mixin, the subclass must:
//
// - Implement _getNext(), _getFirst(), _getLast(), _onLeftArrow(), _onRightArrow()
// _onDownArrow(), _onUpArrow() methods to handle home/end/left/right/up/down keystrokes.
// Next and previous in this context refer to a linear ordering of the descendants used
// by letter key search.
// - Set all descendants' initial tabIndex to "-1"; both initial descendants and any
// descendants added later, by for example addChild()
// - Define childSelector to a function or string that identifies focusable descendant widgets
//
// Also, child widgets must implement a focus() method.
/*=====
// focusedChild: [protected readonly] Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
// _keyNavCodes: Object
// Hash mapping key code (arrow keys and home/end key) to functions to handle those keys.
// Usually not used directly, as subclasses can instead override _onLeftArrow() etc.
_keyNavCodes: {},
=====*/
// tabIndex: String
// Tab index of the container; same as HTML tabIndex attribute.
// Note then when user tabs into the container, focus is immediately
// moved to the first item in the container.
tabIndex: "0",
// childSelector: [protected abstract] Function||String
// Selector (passed to on.selector()) used to identify what to treat as a child widget. Used to monitor
// focus events and set this.focusedChild. Must be set by implementing class. If this is a string
// (ex: "> *") then the implementing class must require dojo/query.
childSelector: null,
postCreate: function(){
this.inherited(arguments);
// Set tabIndex on this.domNode. Will be automatic after #7381 is fixed.
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
if(!this._keyNavCodes){
var keyCodes = this._keyNavCodes = {};
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
keyCodes[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW] = lang.hitch(this, "_onLeftArrow");
keyCodes[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW] = lang.hitch(this, "_onRightArrow");
keyCodes[keys.UP_ARROW] = lang.hitch(this, "_onUpArrow");
keyCodes[keys.DOWN_ARROW] = lang.hitch(this, "_onDownArrow");
}
var self = this,
childSelector = typeof this.childSelector == "string"
? this.childSelector
: lang.hitch(this, "childSelector");
this.own(
on(this.domNode, "keypress", lang.hitch(this, "_onContainerKeypress")),
on(this.domNode, "keydown", lang.hitch(this, "_onContainerKeydown")),
on(this.domNode, "focus", lang.hitch(this, "_onContainerFocus")),
on(this.containerNode, on.selector(childSelector, "focusin"), function(evt){
self._onChildFocus(registry.getEnclosingWidget(this), evt);
})
);
},
_onLeftArrow: function(){
// summary:
// Called on left arrow key, or right arrow key if widget is in RTL mode.
// Should go back to the previous child in horizontal container widgets like Toolbar.
// tags:
// extension
},
_onRightArrow: function(){
// summary:
// Called on right arrow key, or left arrow key if widget is in RTL mode.
// Should go to the next child in horizontal container widgets like Toolbar.
// tags:
// extension
},
_onUpArrow: function(){
// summary:
// Called on up arrow key. Should go to the previous child in vertical container widgets like Menu.
// tags:
// extension
},
_onDownArrow: function(){
// summary:
// Called on down arrow key. Should go to the next child in vertical container widgets like Menu.
// tags:
// extension
},
focus: function(){
// summary:
// Default focus() implementation: focus the first child.
this.focusFirstChild();
},
_getFirstFocusableChild: function(){
// summary:
// Returns first child that can be focused.
// Leverage _getNextFocusableChild() to skip disabled children
return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
},
_getLastFocusableChild: function(){
// summary:
// Returns last child that can be focused.
// Leverage _getNextFocusableChild() to skip disabled children
return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
},
focusFirstChild: function(){
// summary:
// Focus the first focusable child in the container.
// tags:
// protected
this.focusChild(this._getFirstFocusableChild());
},
focusLastChild: function(){
// summary:
// Focus the last focusable child in the container.
// tags:
// protected
this.focusChild(this._getLastFocusableChild());
},
focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last){
// summary:
// Focus specified child widget.
// widget:
// Reference to container's child widget
// last:
// If true and if widget has multiple focusable nodes, focus the
// last one instead of the first one
// tags:
// protected
if(!widget){
return;
}
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild); // used to be used by _MenuBase
}
widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focus
widget.focus(last ? "end" : "start");
// Don't set focusedChild here, because the focus event should trigger a call to _onChildFocus(), which will
// set it. More importantly, _onChildFocus(), which may be executed asynchronously (after this function
// returns) needs to know the old focusedChild to set its tabIndex to -1.
},
_onContainerFocus: function(evt){
// summary:
// Handler for when the container itself gets focus.
// description:
// Initially the container itself has a tabIndex, but when it gets
// focus, switch focus to first child.
//
// TODO for 2.0 (or earlier): Instead of having the container tabbable, always maintain a single child
// widget as tabbable, Requires code in startup(), addChild(), and removeChild().
// That would avoid various issues like #17347.
// tags:
// private
// Note that we can't use _onFocus() because switching focus from the
// _onFocus() handler confuses the focus.js code
// (because it causes _onFocusNode() to be called recursively).
// Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
// Ignore spurious focus events:
// 1. focus on a child widget bubbles on FF
// 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
if(evt.target !== this.domNode || this.focusedChild){
return;
}
this.focus();
},
_onFocus: function(){
// When the container gets focus by being tabbed into, or a descendant gets focus by being clicked,
// set the container's tabIndex to -1 (don't remove as that breaks Safari 4) so that tab or shift-tab
// will go to the fields after/before the container, rather than the container itself
domAttr.set(this.domNode, "tabIndex", "-1");
this.inherited(arguments);
},
_onBlur: function(evt){
// When focus is moved away the container, and its descendant (popup) widgets,
// then restore the container's tabIndex so that user can tab to it again.
// Note that using _onBlur() so that this doesn't happen when focus is shifted
// to one of my child widgets (typically a popup)
// TODO: for 2.0 consider changing this to blur whenever the container blurs, to be truthful that there is
// no focused child at that time.
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
if(this.focusedChild){
this.focusedChild.set("tabIndex", "-1");
this.lastFocusedChild = this.focusedChild;
this._set("focusedChild", null);
}
this.inherited(arguments);
},
_onChildFocus: function(/*dijit/_WidgetBase*/ child){
// summary:
// Called when a child widget gets focus, either by user clicking
// it, or programatically by arrow key handling code.
// description:
// It marks that the current node is the selected one, and the previously
// selected node no longer is.
if(child && child != this.focusedChild){
if(this.focusedChild && !this.focusedChild._destroyed){
// mark that the previously focusable node is no longer focusable
this.focusedChild.set("tabIndex", "-1");
}
// mark that the new node is the currently selected one
child.set("tabIndex", this.tabIndex);
this.lastFocused = child; // back-compat for Tree, remove for 2.0
this._set("focusedChild", child);
}
},
_searchString: "",
// multiCharSearchDuration: Number
// If multiple characters are typed where each keystroke happens within
// multiCharSearchDuration of the previous keystroke,
// search for nodes matching all the keystrokes.
//
// For example, typing "ab" will search for entries starting with
// "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
multiCharSearchDuration: 1000,
onKeyboardSearch: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt, /*String*/ searchString, /*Number*/ numMatches){
// summary:
// When a key is pressed that matches a child item,
// this method is called so that a widget can take appropriate action is necessary.
// tags:
// protected
if(item){
this.focusChild(item);
}
},
_keyboardSearchCompare: function(/*dijit/_WidgetBase*/ item, /*String*/ searchString){
// summary:
// Compares the searchString to the widget's text label, returning:
//
// * -1: a high priority match and stop searching
// * 0: not a match
// * 1: a match but keep looking for a higher priority match
// tags:
// private
var element = item.domNode,
text = item.label || (element.focusNode ? element.focusNode.label : '') || element.innerText || element.textContent || "",
currentString = text.replace(/^\s+/, '').substr(0, searchString.length).toLowerCase();
return (!!searchString.length && currentString == searchString) ? -1 : 0; // stop searching after first match by default
},
_onContainerKeydown: function(evt){
// summary:
// When a key is pressed, if it's an arrow key etc. then it's handled here.
// tags:
// private
var func = this._keyNavCodes[evt.keyCode];
if(func){
func(evt, this.focusedChild);
evt.stopPropagation();
evt.preventDefault();
this._searchString = ''; // so a DOWN_ARROW b doesn't search for ab
}else if(evt.keyCode == keys.SPACE && this._searchTimer && !(evt.ctrlKey || evt.altKey || evt.metaKey)){
evt.stopImmediatePropagation(); // stop a11yclick and _HasDropdown from seeing SPACE if we're doing keyboard searching
evt.preventDefault(); // stop IE from scrolling, and most browsers (except FF) from sending keypress
this._keyboardSearch(evt, ' ');
}
},
_onContainerKeypress: function(evt){
// summary:
// When a printable key is pressed, it's handled here, searching by letter.
// tags:
// private
// Ignore:
// - duplicate events on firefox (ex: arrow key that will be handled by keydown handler)
// - control sequences like CMD-Q.
// - the SPACE key (only occurs on FF)
//
// Note: if there's no search in progress, then SPACE should be ignored. If there is a search
// in progress, then SPACE is handled in _onContainerKeyDown.
if(evt.charCode <= keys.SPACE || evt.ctrlKey || evt.altKey || evt.metaKey){
return;
}
evt.preventDefault();
evt.stopPropagation();
this._keyboardSearch(evt, String.fromCharCode(evt.charCode).toLowerCase());
},
_keyboardSearch: function(/*Event*/ evt, /*String*/ keyChar){
// summary:
// Perform a search of the widget's options based on the user's keyboard activity
// description:
// Called on keypress (and sometimes keydown), searches through this widget's children
// looking for items that match the user's typed search string. Multiple characters
// typed within 1 sec of each other are combined for multicharacter searching.
// tags:
// private
var
matchedItem = null,
searchString,
numMatches = 0,
search = lang.hitch(this, function(){
if(this._searchTimer){
this._searchTimer.remove();
}
this._searchString += keyChar;
var allSameLetter = /^(.)\1*$/.test(this._searchString);
var searchLen = allSameLetter ? 1 : this._searchString.length;
searchString = this._searchString.substr(0, searchLen);
// commented out code block to search again if the multichar search fails after a smaller timeout
//this._searchTimer = this.defer(function(){ // this is the "failure" timeout
// this._typingSlowly = true; // if the search fails, then treat as a full timeout
// this._searchTimer = this.defer(function(){ // this is the "success" timeout
// this._searchTimer = null;
// this._searchString = '';
// }, this.multiCharSearchDuration >> 1);
//}, this.multiCharSearchDuration >> 1);
this._searchTimer = this.defer(function(){ // this is the "success" timeout
this._searchTimer = null;
this._searchString = '';
}, this.multiCharSearchDuration);
var currentItem = this.focusedChild || null;
if(searchLen == 1 || !currentItem){
currentItem = this._getNextFocusableChild(currentItem, 1); // skip current
if(!currentItem){
return;
} // no items
}
var stop = currentItem;
do{
var rc = this._keyboardSearchCompare(currentItem, searchString);
if(!!rc && numMatches++ == 0){
matchedItem = currentItem;
}
if(rc == -1){ // priority match
numMatches = -1;
break;
}
currentItem = this._getNextFocusableChild(currentItem, 1);
}while(currentItem != stop);
// commented out code block to search again if the multichar search fails after a smaller timeout
//if(!numMatches && (this._typingSlowly || searchLen == 1)){
// this._searchString = '';
// if(searchLen > 1){
// // if no matches and they're typing slowly, then go back to first letter searching
// search();
// }
//}
});
search();
// commented out code block to search again if the multichar search fails after a smaller timeout
//this._typingSlowly = false;
this.onKeyboardSearch(matchedItem, evt, searchString, numMatches);
},
_onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
// Used to be used by MenuBase.js (remove for 2.0)
// tags:
// protected
},
_getNextFocusableChild: function(child, dir){
// summary:
// Returns the next or previous focusable descendant, compared to "child".
// Implements and extends _KeyNavMixin._getNextFocusableChild() for a _Container.
// child: Widget
// The current widget
// dir: Integer
// - 1 = after
// - -1 = before
// tags:
// abstract extension
var wrappedValue = child;
do{
if(!child){
child = this[dir > 0 ? "_getFirst" : "_getLast"]();
if(!child){ break; }
}else{
child = this._getNext(child, dir);
}
if(child != null && child != wrappedValue && child.isFocusable()){
return child; // dijit/_WidgetBase
}
}while(child != wrappedValue);
// no focusable child found
return null; // dijit/_WidgetBase
},
_getFirst: function(){
// summary:
// Returns the first child.
// tags:
// abstract extension
return null; // dijit/_WidgetBase
},
_getLast: function(){
// summary:
// Returns the last descendant.
// tags:
// abstract extension
return null; // dijit/_WidgetBase
},
_getNext: function(child, dir){
// summary:
// Returns the next descendant, compared to "child".
// child: Widget
// The current widget
// dir: Integer
// - 1 = after
// - -1 = before
// tags:
// abstract extension
if(child){
child = child.domNode;
while(child){
child = child[dir < 0 ? "previousSibling" : "nextSibling"];
if(child && "getAttribute" in child){
var w = registry.byNode(child);
if(w){
return w; // dijit/_WidgetBase
}
}
}
}
return null; // dijit/_WidgetBase
}
});
});
@@ -1,487 +0,0 @@
define("dijit/_KeyNavMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/keys", // keys.END keys.HOME, keys.LEFT_ARROW etc.
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dijit/registry",
"dijit/_FocusMixin" // to make _onBlur() work
], function(array, declare, domAttr, keys, lang, on, registry, _FocusMixin){
// module:
// dijit/_KeyNavMixin
return declare("dijit._KeyNavMixin", _FocusMixin, {
// summary:
// A mixin to allow arrow key and letter key navigation of child or descendant widgets.
// It can be used by dijit/_Container based widgets with a flat list of children,
// or more complex widgets like dijit/Tree.
//
// To use this mixin, the subclass must:
//
// - Implement _getNext(), _getFirst(), _getLast(), _onLeftArrow(), _onRightArrow()
// _onDownArrow(), _onUpArrow() methods to handle home/end/left/right/up/down keystrokes.
// Next and previous in this context refer to a linear ordering of the descendants used
// by letter key search.
// - Set all descendants' initial tabIndex to "-1"; both initial descendants and any
// descendants added later, by for example addChild()
// - Define childSelector to a function or string that identifies focusable descendant widgets
//
// Also, child widgets must implement a focus() method.
/*=====
// focusedChild: [protected readonly] Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
// _keyNavCodes: Object
// Hash mapping key code (arrow keys and home/end key) to functions to handle those keys.
// Usually not used directly, as subclasses can instead override _onLeftArrow() etc.
_keyNavCodes: {},
=====*/
// tabIndex: String
// Tab index of the container; same as HTML tabIndex attribute.
// Note then when user tabs into the container, focus is immediately
// moved to the first item in the container.
tabIndex: "0",
// childSelector: [protected abstract] Function||String
// Selector (passed to on.selector()) used to identify what to treat as a child widget. Used to monitor
// focus events and set this.focusedChild. Must be set by implementing class. If this is a string
// (ex: "> *") then the implementing class must require dojo/query.
childSelector: null,
postCreate: function(){
this.inherited(arguments);
// Set tabIndex on this.domNode. Will be automatic after #7381 is fixed.
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
if(!this._keyNavCodes){
var keyCodes = this._keyNavCodes = {};
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
keyCodes[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW] = lang.hitch(this, "_onLeftArrow");
keyCodes[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW] = lang.hitch(this, "_onRightArrow");
keyCodes[keys.UP_ARROW] = lang.hitch(this, "_onUpArrow");
keyCodes[keys.DOWN_ARROW] = lang.hitch(this, "_onDownArrow");
}
var self = this,
childSelector = typeof this.childSelector == "string"
? this.childSelector
: lang.hitch(this, "childSelector");
this.own(
on(this.domNode, "keypress", lang.hitch(this, "_onContainerKeypress")),
on(this.domNode, "keydown", lang.hitch(this, "_onContainerKeydown")),
on(this.domNode, "focus", lang.hitch(this, "_onContainerFocus")),
on(this.containerNode, on.selector(childSelector, "focusin"), function(evt){
self._onChildFocus(registry.getEnclosingWidget(this), evt);
})
);
},
_onLeftArrow: function(){
// summary:
// Called on left arrow key, or right arrow key if widget is in RTL mode.
// Should go back to the previous child in horizontal container widgets like Toolbar.
// tags:
// extension
},
_onRightArrow: function(){
// summary:
// Called on right arrow key, or left arrow key if widget is in RTL mode.
// Should go to the next child in horizontal container widgets like Toolbar.
// tags:
// extension
},
_onUpArrow: function(){
// summary:
// Called on up arrow key. Should go to the previous child in vertical container widgets like Menu.
// tags:
// extension
},
_onDownArrow: function(){
// summary:
// Called on down arrow key. Should go to the next child in vertical container widgets like Menu.
// tags:
// extension
},
focus: function(){
// summary:
// Default focus() implementation: focus the first child.
this.focusFirstChild();
},
_getFirstFocusableChild: function(){
// summary:
// Returns first child that can be focused.
// Leverage _getNextFocusableChild() to skip disabled children
return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
},
_getLastFocusableChild: function(){
// summary:
// Returns last child that can be focused.
// Leverage _getNextFocusableChild() to skip disabled children
return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
},
focusFirstChild: function(){
// summary:
// Focus the first focusable child in the container.
// tags:
// protected
this.focusChild(this._getFirstFocusableChild());
},
focusLastChild: function(){
// summary:
// Focus the last focusable child in the container.
// tags:
// protected
this.focusChild(this._getLastFocusableChild());
},
focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last){
// summary:
// Focus specified child widget.
// widget:
// Reference to container's child widget
// last:
// If true and if widget has multiple focusable nodes, focus the
// last one instead of the first one
// tags:
// protected
if(!widget){
return;
}
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild); // used to be used by _MenuBase
}
widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focus
widget.focus(last ? "end" : "start");
// Don't set focusedChild here, because the focus event should trigger a call to _onChildFocus(), which will
// set it. More importantly, _onChildFocus(), which may be executed asynchronously (after this function
// returns) needs to know the old focusedChild to set its tabIndex to -1.
},
_onContainerFocus: function(evt){
// summary:
// Handler for when the container itself gets focus.
// description:
// Initially the container itself has a tabIndex, but when it gets
// focus, switch focus to first child.
//
// TODO for 2.0 (or earlier): Instead of having the container tabbable, always maintain a single child
// widget as tabbable, Requires code in startup(), addChild(), and removeChild().
// That would avoid various issues like #17347.
// tags:
// private
// Note that we can't use _onFocus() because switching focus from the
// _onFocus() handler confuses the focus.js code
// (because it causes _onFocusNode() to be called recursively).
// Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
// Ignore spurious focus events:
// 1. focus on a child widget bubbles on FF
// 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
if(evt.target !== this.domNode || this.focusedChild){
return;
}
this.focus();
},
_onFocus: function(){
// When the container gets focus by being tabbed into, or a descendant gets focus by being clicked,
// set the container's tabIndex to -1 (don't remove as that breaks Safari 4) so that tab or shift-tab
// will go to the fields after/before the container, rather than the container itself
domAttr.set(this.domNode, "tabIndex", "-1");
this.inherited(arguments);
},
_onBlur: function(evt){
// When focus is moved away the container, and its descendant (popup) widgets,
// then restore the container's tabIndex so that user can tab to it again.
// Note that using _onBlur() so that this doesn't happen when focus is shifted
// to one of my child widgets (typically a popup)
// TODO: for 2.0 consider changing this to blur whenever the container blurs, to be truthful that there is
// no focused child at that time.
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
if(this.focusedChild){
this.focusedChild.set("tabIndex", "-1");
this.lastFocusedChild = this.focusedChild;
this._set("focusedChild", null);
}
this.inherited(arguments);
},
_onChildFocus: function(/*dijit/_WidgetBase*/ child){
// summary:
// Called when a child widget gets focus, either by user clicking
// it, or programatically by arrow key handling code.
// description:
// It marks that the current node is the selected one, and the previously
// selected node no longer is.
if(child && child != this.focusedChild){
if(this.focusedChild && !this.focusedChild._destroyed){
// mark that the previously focusable node is no longer focusable
this.focusedChild.set("tabIndex", "-1");
}
// mark that the new node is the currently selected one
child.set("tabIndex", this.tabIndex);
this.lastFocused = child; // back-compat for Tree, remove for 2.0
this._set("focusedChild", child);
}
},
_searchString: "",
// multiCharSearchDuration: Number
// If multiple characters are typed where each keystroke happens within
// multiCharSearchDuration of the previous keystroke,
// search for nodes matching all the keystrokes.
//
// For example, typing "ab" will search for entries starting with
// "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
multiCharSearchDuration: 1000,
onKeyboardSearch: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt, /*String*/ searchString, /*Number*/ numMatches){
// summary:
// When a key is pressed that matches a child item,
// this method is called so that a widget can take appropriate action is necessary.
// tags:
// protected
if(item){
this.focusChild(item);
}
},
_keyboardSearchCompare: function(/*dijit/_WidgetBase*/ item, /*String*/ searchString){
// summary:
// Compares the searchString to the widget's text label, returning:
//
// * -1: a high priority match and stop searching
// * 0: not a match
// * 1: a match but keep looking for a higher priority match
// tags:
// private
var element = item.domNode,
text = item.label || (element.focusNode ? element.focusNode.label : '') || element.innerText || element.textContent || "",
currentString = text.replace(/^\s+/, '').substr(0, searchString.length).toLowerCase();
return (!!searchString.length && currentString == searchString) ? -1 : 0; // stop searching after first match by default
},
_onContainerKeydown: function(evt){
// summary:
// When a key is pressed, if it's an arrow key etc. then it's handled here.
// tags:
// private
var func = this._keyNavCodes[evt.keyCode];
if(func){
func(evt, this.focusedChild);
evt.stopPropagation();
evt.preventDefault();
this._searchString = ''; // so a DOWN_ARROW b doesn't search for ab
}else if(evt.keyCode == keys.SPACE && this._searchTimer && !(evt.ctrlKey || evt.altKey || evt.metaKey)){
evt.stopImmediatePropagation(); // stop a11yclick and _HasDropdown from seeing SPACE if we're doing keyboard searching
evt.preventDefault(); // stop IE from scrolling, and most browsers (except FF) from sending keypress
this._keyboardSearch(evt, ' ');
}
},
_onContainerKeypress: function(evt){
// summary:
// When a printable key is pressed, it's handled here, searching by letter.
// tags:
// private
// Ignore:
// - duplicate events on firefox (ex: arrow key that will be handled by keydown handler)
// - control sequences like CMD-Q.
// - the SPACE key (only occurs on FF)
//
// Note: if there's no search in progress, then SPACE should be ignored. If there is a search
// in progress, then SPACE is handled in _onContainerKeyDown.
if(evt.charCode <= keys.SPACE || evt.ctrlKey || evt.altKey || evt.metaKey){
return;
}
evt.preventDefault();
evt.stopPropagation();
this._keyboardSearch(evt, String.fromCharCode(evt.charCode).toLowerCase());
},
_keyboardSearch: function(/*Event*/ evt, /*String*/ keyChar){
// summary:
// Perform a search of the widget's options based on the user's keyboard activity
// description:
// Called on keypress (and sometimes keydown), searches through this widget's children
// looking for items that match the user's typed search string. Multiple characters
// typed within 1 sec of each other are combined for multicharacter searching.
// tags:
// private
var
matchedItem = null,
searchString,
numMatches = 0,
search = lang.hitch(this, function(){
if(this._searchTimer){
this._searchTimer.remove();
}
this._searchString += keyChar;
var allSameLetter = /^(.)\1*$/.test(this._searchString);
var searchLen = allSameLetter ? 1 : this._searchString.length;
searchString = this._searchString.substr(0, searchLen);
// commented out code block to search again if the multichar search fails after a smaller timeout
//this._searchTimer = this.defer(function(){ // this is the "failure" timeout
// this._typingSlowly = true; // if the search fails, then treat as a full timeout
// this._searchTimer = this.defer(function(){ // this is the "success" timeout
// this._searchTimer = null;
// this._searchString = '';
// }, this.multiCharSearchDuration >> 1);
//}, this.multiCharSearchDuration >> 1);
this._searchTimer = this.defer(function(){ // this is the "success" timeout
this._searchTimer = null;
this._searchString = '';
}, this.multiCharSearchDuration);
var currentItem = this.focusedChild || null;
if(searchLen == 1 || !currentItem){
currentItem = this._getNextFocusableChild(currentItem, 1); // skip current
if(!currentItem){
return;
} // no items
}
var stop = currentItem;
do{
var rc = this._keyboardSearchCompare(currentItem, searchString);
if(!!rc && numMatches++ == 0){
matchedItem = currentItem;
}
if(rc == -1){ // priority match
numMatches = -1;
break;
}
currentItem = this._getNextFocusableChild(currentItem, 1);
}while(currentItem != stop);
// commented out code block to search again if the multichar search fails after a smaller timeout
//if(!numMatches && (this._typingSlowly || searchLen == 1)){
// this._searchString = '';
// if(searchLen > 1){
// // if no matches and they're typing slowly, then go back to first letter searching
// search();
// }
//}
});
search();
// commented out code block to search again if the multichar search fails after a smaller timeout
//this._typingSlowly = false;
this.onKeyboardSearch(matchedItem, evt, searchString, numMatches);
},
_onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
// Used to be used by MenuBase.js (remove for 2.0)
// tags:
// protected
},
_getNextFocusableChild: function(child, dir){
// summary:
// Returns the next or previous focusable descendant, compared to "child".
// Implements and extends _KeyNavMixin._getNextFocusableChild() for a _Container.
// child: Widget
// The current widget
// dir: Integer
// - 1 = after
// - -1 = before
// tags:
// abstract extension
var wrappedValue = child;
do{
if(!child){
child = this[dir > 0 ? "_getFirst" : "_getLast"]();
if(!child){ break; }
}else{
child = this._getNext(child, dir);
}
if(child != null && child != wrappedValue && child.isFocusable()){
return child; // dijit/_WidgetBase
}
}while(child != wrappedValue);
// no focusable child found
return null; // dijit/_WidgetBase
},
_getFirst: function(){
// summary:
// Returns the first child.
// tags:
// abstract extension
return null; // dijit/_WidgetBase
},
_getLast: function(){
// summary:
// Returns the last descendant.
// tags:
// abstract extension
return null; // dijit/_WidgetBase
},
_getNext: function(child, dir){
// summary:
// Returns the next descendant, compared to "child".
// child: Widget
// The current widget
// dir: Integer
// - 1 = after
// - -1 = before
// tags:
// abstract extension
if(child){
child = child.domNode;
while(child){
child = child[dir < 0 ? "previousSibling" : "nextSibling"];
if(child && "getAttribute" in child){
var w = registry.byNode(child);
if(w){
return w; // dijit/_WidgetBase
}
}
}
}
return null; // dijit/_WidgetBase
}
});
});
@@ -1,186 +1,466 @@
//>>built
define("dijit/_MenuBase",["dojo/_base/array","dojo/_base/declare","dojo/dom","dojo/dom-attr","dojo/dom-class","dojo/_base/lang","dojo/mouse","dojo/on","dojo/window","./a11yclick","./registry","./_Widget","./_CssStateMixin","./_KeyNavContainer","./_TemplatedMixin"],function(_1,_2,_3,_4,_5,_6,_7,on,_8,_9,_a,_b,_c,_d,_e){
return _2("dijit._MenuBase",[_b,_e,_d,_c],{selected:null,_setSelectedAttr:function(_f){
if(this.selected!=_f){
if(this.selected){
this.selected._setSelected(false);
this._onChildDeselect(this.selected);
}
if(_f){
_f._setSelected(true);
}
this._set("selected",_f);
}
},activated:false,_setActivatedAttr:function(val){
_5.toggle(this.domNode,"dijitMenuActive",val);
_5.toggle(this.domNode,"dijitMenuPassive",!val);
this._set("activated",val);
},parentMenu:null,popupDelay:500,passivePopupDelay:Infinity,autoFocus:false,childSelector:function(_10){
var _11=_a.byNode(_10);
return _10.parentNode==this.containerNode&&_11&&_11.focus;
},postCreate:function(){
var _12=this,_13=typeof this.childSelector=="string"?this.childSelector:_6.hitch(this,"childSelector");
this.own(on(this.containerNode,on.selector(_13,_7.enter),function(){
_12.onItemHover(_a.byNode(this));
}),on(this.containerNode,on.selector(_13,_7.leave),function(){
_12.onItemUnhover(_a.byNode(this));
}),on(this.containerNode,on.selector(_13,_9),function(evt){
_12.onItemClick(_a.byNode(this),evt);
evt.stopPropagation();
}),on(this.containerNode,on.selector(_13,"focusin"),function(){
_12._onItemFocus(_a.byNode(this));
}));
this.inherited(arguments);
},onKeyboardSearch:function(_14,evt,_15,_16){
this.inherited(arguments);
if(!!_14&&(_16==-1||(!!_14.popup&&_16==1))){
this.onItemClick(_14,evt);
}
},_keyboardSearchCompare:function(_17,_18){
if(!!_17.shortcutKey){
return _18==_17.shortcutKey.toLowerCase()?-1:0;
}
return this.inherited(arguments)?1:0;
},onExecute:function(){
},onCancel:function(){
},_moveToPopup:function(evt){
if(this.focusedChild&&this.focusedChild.popup&&!this.focusedChild.disabled){
this.onItemClick(this.focusedChild,evt);
}else{
var _19=this._getTopMenu();
if(_19&&_19._isMenuBar){
_19.focusNext();
}
}
},_onPopupHover:function(){
this.set("selected",this.currentPopupItem);
this._stopPendingCloseTimer();
},onItemHover:function(_1a){
if(this.activated){
this.set("selected",_1a);
if(_1a.popup&&!_1a.disabled&&!this.hover_timer){
this.hover_timer=this.defer(function(){
this._openItemPopup(_1a);
},this.popupDelay);
}
}else{
if(this.passivePopupDelay<Infinity){
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.passive_hover_timer=this.defer(function(){
this.onItemClick(_1a,{type:"click"});
},this.passivePopupDelay);
}
}
this._hoveredChild=_1a;
_1a._set("hovering",true);
},_onChildDeselect:function(_1b){
this._stopPopupTimer();
if(this.currentPopupItem==_1b){
this._stopPendingCloseTimer();
this._pendingClose_timer=this.defer(function(){
this._pendingClose_timer=null;
this.currentPopupItem=null;
_1b._closePopup();
},this.popupDelay);
}
},onItemUnhover:function(_1c){
if(this._hoveredChild==_1c){
this._hoveredChild=null;
}
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
this.passive_hover_timer=null;
}
_1c._set("hovering",false);
},_stopPopupTimer:function(){
if(this.hover_timer){
this.hover_timer=this.hover_timer.remove();
}
},_stopPendingCloseTimer:function(){
if(this._pendingClose_timer){
this._pendingClose_timer=this._pendingClose_timer.remove();
}
},_getTopMenu:function(){
for(var top=this;top.parentMenu;top=top.parentMenu){
}
return top;
},onItemClick:function(_1d,evt){
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.focusChild(_1d);
if(_1d.disabled){
return false;
}
if(_1d.popup){
this.set("selected",_1d);
this.set("activated",true);
var _1e=/^key/.test(evt._origType||evt.type)||(evt.clientX==0&&evt.clientY==0);
this._openItemPopup(_1d,_1e);
}else{
this.onExecute();
_1d._onClick?_1d._onClick(evt):_1d.onClick(evt);
}
},_openItemPopup:function(_1f,_20){
if(_1f==this.currentPopupItem){
return;
}
if(this.currentPopupItem){
this._stopPendingCloseTimer();
this.currentPopupItem._closePopup();
}
this._stopPopupTimer();
var _21=_1f.popup;
_21.parentMenu=this;
this.own(this._mouseoverHandle=on.once(_21.domNode,"mouseover",_6.hitch(this,"_onPopupHover")));
var _22=this;
_1f._openPopup({parent:this,orient:this._orient||["after","before"],onCancel:function(){
if(_20){
_22.focusChild(_1f);
}
_22._cleanUp();
},onExecute:_6.hitch(this,"_cleanUp",true),onClose:function(){
if(_22._mouseoverHandle){
_22._mouseoverHandle.remove();
delete _22._mouseoverHandle;
}
}},_20);
this.currentPopupItem=_1f;
},onOpen:function(){
this.isShowingNow=true;
this.set("activated",true);
},onClose:function(){
this.set("activated",false);
this.set("selected",null);
this.isShowingNow=false;
this.parentMenu=null;
},_closeChild:function(){
this._stopPopupTimer();
if(this.currentPopupItem){
if(this.focused){
_4.set(this.selected.focusNode,"tabIndex",this.tabIndex);
this.selected.focusNode.focus();
}
this.currentPopupItem._closePopup();
this.currentPopupItem=null;
}
},_onItemFocus:function(_23){
if(this._hoveredChild&&this._hoveredChild!=_23){
this.onItemUnhover(this._hoveredChild);
}
this.set("selected",_23);
},_onBlur:function(){
this._cleanUp(true);
this.inherited(arguments);
},_cleanUp:function(_24){
this._closeChild();
if(typeof this.isShowingNow=="undefined"){
this.set("activated",false);
}
if(_24){
this.set("selected",null);
}
}});
define([
"dojo/_base/array", // array.indexOf
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant domClass.replace
"dojo/dom-attr",
"dojo/dom-class", // domClass.replace
"dojo/_base/lang", // lang.hitch
"dojo/mouse", // mouse.enter, mouse.leave
"dojo/on",
"dojo/window",
"./a11yclick",
"./registry",
"./_Widget",
"./_CssStateMixin",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(array, declare, dom, domAttr, domClass, lang, mouse, on, winUtils, a11yclick,
registry, _Widget, _CssStateMixin, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/_MenuBase
return declare("dijit._MenuBase", [_Widget, _TemplatedMixin, _KeyNavContainer, _CssStateMixin], {
// summary:
// Abstract base class for Menu and MenuBar.
// Subclass should implement _onUpArrow(), _onDownArrow(), _onLeftArrow(), and _onRightArrow().
// selected: dijit/MenuItem
// Currently selected (a.k.a. highlighted) MenuItem, or null if no MenuItem is selected.
// If a submenu is open, will be set to MenuItem that displayed the submenu. OTOH, if
// this Menu is in passive mode (i.e. hasn't been clicked yet), will be null, because
// "selected" is not merely "hovered".
selected: null,
_setSelectedAttr: function(item){
if(this.selected != item){
if(this.selected){
this.selected._setSelected(false);
this._onChildDeselect(this.selected);
}
if(item){
item._setSelected(true);
}
this._set("selected", item);
}
},
// activated: [readonly] Boolean
// This Menu has been clicked (mouse or via space/arrow key) or opened as a submenu,
// so mere mouseover will open submenus. Focusing a menu via TAB does NOT automatically make it active
// since TAB is a navigation operation and not a selection one.
// For Windows apps, pressing the ALT key focuses the menubar menus (similar to TAB navigation) but the
// menu is not active (ie no dropdown) until an item is clicked.
activated: false,
_setActivatedAttr: function(val){
domClass.toggle(this.domNode, "dijitMenuActive", val);
domClass.toggle(this.domNode, "dijitMenuPassive", !val);
this._set("activated", val);
},
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// After a menu has been activated (by clicking on it etc.), number of milliseconds before hovering
// (without clicking) another MenuItem causes that MenuItem's popup to automatically open.
popupDelay: 500,
// passivePopupDelay: Integer
// For a passive (unclicked) Menu, number of milliseconds before hovering (without clicking) will cause
// the popup to open. Default is Infinity, meaning you need to click the menu to open it.
passivePopupDelay: Infinity,
// autoFocus: Boolean
// A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
// or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
autoFocus: false,
childSelector: function(/*DOMNode*/ node){
// summary:
// Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
// like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
// tags:
// protected
var widget = registry.byNode(node);
return node.parentNode == this.containerNode && widget && widget.focus;
},
postCreate: function(){
var self = this,
matches = typeof this.childSelector == "string" ? this.childSelector : lang.hitch(this, "childSelector");
this.own(
on(this.containerNode, on.selector(matches, mouse.enter), function(){
self.onItemHover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, mouse.leave), function(){
self.onItemUnhover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, a11yclick), function(evt){
self.onItemClick(registry.byNode(this), evt);
evt.stopPropagation();
}),
on(this.containerNode, on.selector(matches, "focusin"), function(){
self._onItemFocus(registry.byNode(this));
})
);
this.inherited(arguments);
},
onKeyboardSearch: function(/*MenuItem*/ item, /*Event*/ evt, /*String*/ searchString, /*Number*/ numMatches){
// summary:
// Attach point for notification about when a menu item has been searched for
// via the keyboard search mechanism.
// tags:
// protected
this.inherited(arguments);
if(!!item && (numMatches == -1 || (!!item.popup && numMatches == 1))){
this.onItemClick(item, evt);
}
},
_keyboardSearchCompare: function(/*dijit/_WidgetBase*/ item, /*String*/ searchString){
// summary:
// Compares the searchString to the widget's text label, returning:
// -1: a high priority match and stop searching
// 0: no match
// 1: a match but keep looking for a higher priority match
// tags:
// private
if(!!item.shortcutKey){
// accessKey matches have priority
return searchString == item.shortcutKey.toLowerCase() ? -1 : 0;
}
return this.inherited(arguments) ? 1 : 0; // change return value of -1 to 1 so that searching continues
},
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.onItemClick(this.focusedChild, evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
_onPopupHover: function(/*Event*/ /*===== evt =====*/){
// summary:
// This handler is called when the mouse moves over the popup.
// tags:
// private
// if the mouse hovers over a menu popup that is in pending-close state,
// then stop the close operation.
// This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
// highlight the parent menu item pointing to this popup (in case user temporarily moused over another MenuItem)
this.set("selected", this.currentPopupItem);
// cancel the pending close (if there is one) (in case user temporarily moused over another MenuItem)
this._stopPendingCloseTimer();
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) opening it from a parent menu (which automatically activates it)
if(this.activated){
this.set("selected", item);
if(item.popup && !item.disabled && !this.hover_timer){
this.hover_timer = this.defer(function(){
this._openItemPopup(item);
}, this.popupDelay);
}
}else if(this.passivePopupDelay < Infinity){
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.passive_hover_timer = this.defer(function(){
this.onItemClick(item, {type: "click"});
}, this.passivePopupDelay);
}
this._hoveredChild = item;
item._set("hovering", true);
},
_onChildDeselect: function(item){
// summary:
// Called when a child MenuItem becomes deselected. Setup timer to close its popup.
this._stopPopupTimer();
// Setup timer to close all popups that are open and descendants of this menu.
// Will be canceled if user quickly moves the mouse over the popup.
if(this.currentPopupItem == item){
this._stopPendingCloseTimer();
this._pendingClose_timer = this.defer(function(){
this._pendingClose_timer = null;
this.currentPopupItem = null;
item._closePopup(); // this calls onClose
}, this.popupDelay);
}
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this._hoveredChild == item){
this._hoveredChild = null;
}
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
this.passive_hover_timer = null;
}
item._set("hovering", false);
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
this.hover_timer = this.hover_timer.remove();
}
},
_stopPendingCloseTimer: function(){
// summary:
// Cancels the pending-close timer because the close has been preempted
// tags:
// private
if(this._pendingClose_timer){
this._pendingClose_timer = this._pendingClose_timer.remove();
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top = this; top.parentMenu; top = top.parentMenu){}
return top;
},
onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.focusChild(item);
if(item.disabled){
return false;
}
if(item.popup){
this.set("selected", item);
this.set("activated", true);
var byKeyboard = /^key/.test(evt._origType || evt.type) ||
(evt.clientX == 0 && evt.clientY == 0); // detects accessKey like ALT+SHIFT+F, where type is "click"
this._openItemPopup(item, byKeyboard);
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item._onClick ? item._onClick(evt) : item.onClick(evt);
}
},
_openItemPopup: function(/*dijit/MenuItem*/ from_item, /*Boolean*/ focus){
// summary:
// Open the popup to the side of/underneath the current menu item, and optionally focus first item
// tags:
// protected
if(from_item == this.currentPopupItem){
// Specified popup is already being shown, so just return
return;
}
if(this.currentPopupItem){
// If another popup is currently shown, then close it
this._stopPendingCloseTimer();
this.currentPopupItem._closePopup();
}
this._stopPopupTimer();
var popup = from_item.popup;
popup.parentMenu = this;
// detect mouseover of the popup to handle lazy mouse movements that temporarily focus other menu items\c
this.own(this._mouseoverHandle = on.once(popup.domNode, "mouseover", lang.hitch(this, "_onPopupHover")));
var self = this;
from_item._openPopup({
parent: this,
orient: this._orient || ["after", "before"],
onCancel: function(){ // called when the child menu is canceled
if(focus){
// put focus back on my node before focused node is hidden
self.focusChild(from_item);
}
// close the submenu (be sure this is done _after_ focus is moved)
self._cleanUp();
},
onExecute: lang.hitch(this, "_cleanUp", true),
onClose: function(){
// Remove handler created by onItemHover
if(self._mouseoverHandle){
self._mouseoverHandle.remove();
delete self._mouseoverHandle;
}
}
}, focus);
this.currentPopupItem = from_item;
// TODO: focusing a popup should clear tabIndex on Menu (and it's child MenuItems), so that neither
// TAB nor SHIFT-TAB returns to the menu. Only ESC or ENTER should return to the menu.
},
onOpen: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback when this menu is opened.
// This is called by the popup manager as notification that the menu
// was opened.
// tags:
// private
this.isShowingNow = true;
this.set("activated", true);
},
onClose: function(){
// summary:
// Callback when this menu is closed.
// This is called by the popup manager as notification that the menu
// was closed.
// tags:
// private
this.set("activated", false);
this.set("selected", null);
this.isShowingNow = false;
this.parentMenu = null;
},
_closeChild: function(){
// summary:
// Called when submenu is clicked or focus is lost. Close hierarchy of menus.
// tags:
// private
this._stopPopupTimer();
if(this.currentPopupItem){
// If focus is on a descendant MenuItem then move focus to me,
// because IE doesn't like it when you display:none a node with focus,
// and also so keyboard users don't lose control.
// Likely, immediately after a user defined onClick handler will move focus somewhere
// else, like a Dialog.
if(this.focused){
domAttr.set(this.selected.focusNode, "tabIndex", this.tabIndex);
this.selected.focusNode.focus();
}
// Close all popups that are open and descendants of this menu
this.currentPopupItem._closePopup();
this.currentPopupItem = null;
}
},
_onItemFocus: function(/*MenuItem*/ item){
// summary:
// Called when child of this Menu gets focus from:
//
// 1. clicking it
// 2. tabbing into it
// 3. being opened by a parent menu.
//
// This is not called just from mouse hover.
if(this._hoveredChild && this._hoveredChild != item){
this.onItemUnhover(this._hoveredChild); // any previous mouse movement is trumped by focus selection
}
this.set("selected", item);
},
_onBlur: function(){
// summary:
// Called when focus is moved away from this Menu and it's submenus.
// tags:
// protected
this._cleanUp(true);
this.inherited(arguments);
},
_cleanUp: function(/*Boolean*/ clearSelectedItem){
// summary:
// Called when the user is done with this menu. Closes hierarchy of menus.
// tags:
// private
this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
this.set("activated", false);
}
if(clearSelectedItem){
this.set("selected", null);
}
}
});
});
@@ -1,466 +0,0 @@
define("dijit/_MenuBase", [
"dojo/_base/array", // array.indexOf
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant domClass.replace
"dojo/dom-attr",
"dojo/dom-class", // domClass.replace
"dojo/_base/lang", // lang.hitch
"dojo/mouse", // mouse.enter, mouse.leave
"dojo/on",
"dojo/window",
"./a11yclick",
"./registry",
"./_Widget",
"./_CssStateMixin",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(array, declare, dom, domAttr, domClass, lang, mouse, on, winUtils, a11yclick,
registry, _Widget, _CssStateMixin, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/_MenuBase
return declare("dijit._MenuBase", [_Widget, _TemplatedMixin, _KeyNavContainer, _CssStateMixin], {
// summary:
// Abstract base class for Menu and MenuBar.
// Subclass should implement _onUpArrow(), _onDownArrow(), _onLeftArrow(), and _onRightArrow().
// selected: dijit/MenuItem
// Currently selected (a.k.a. highlighted) MenuItem, or null if no MenuItem is selected.
// If a submenu is open, will be set to MenuItem that displayed the submenu. OTOH, if
// this Menu is in passive mode (i.e. hasn't been clicked yet), will be null, because
// "selected" is not merely "hovered".
selected: null,
_setSelectedAttr: function(item){
if(this.selected != item){
if(this.selected){
this.selected._setSelected(false);
this._onChildDeselect(this.selected);
}
if(item){
item._setSelected(true);
}
this._set("selected", item);
}
},
// activated: [readonly] Boolean
// This Menu has been clicked (mouse or via space/arrow key) or opened as a submenu,
// so mere mouseover will open submenus. Focusing a menu via TAB does NOT automatically make it active
// since TAB is a navigation operation and not a selection one.
// For Windows apps, pressing the ALT key focuses the menubar menus (similar to TAB navigation) but the
// menu is not active (ie no dropdown) until an item is clicked.
activated: false,
_setActivatedAttr: function(val){
domClass.toggle(this.domNode, "dijitMenuActive", val);
domClass.toggle(this.domNode, "dijitMenuPassive", !val);
this._set("activated", val);
},
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// After a menu has been activated (by clicking on it etc.), number of milliseconds before hovering
// (without clicking) another MenuItem causes that MenuItem's popup to automatically open.
popupDelay: 500,
// passivePopupDelay: Integer
// For a passive (unclicked) Menu, number of milliseconds before hovering (without clicking) will cause
// the popup to open. Default is Infinity, meaning you need to click the menu to open it.
passivePopupDelay: Infinity,
// autoFocus: Boolean
// A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
// or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
autoFocus: false,
childSelector: function(/*DOMNode*/ node){
// summary:
// Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
// like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
// tags:
// protected
var widget = registry.byNode(node);
return node.parentNode == this.containerNode && widget && widget.focus;
},
postCreate: function(){
var self = this,
matches = typeof this.childSelector == "string" ? this.childSelector : lang.hitch(this, "childSelector");
this.own(
on(this.containerNode, on.selector(matches, mouse.enter), function(){
self.onItemHover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, mouse.leave), function(){
self.onItemUnhover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, a11yclick), function(evt){
self.onItemClick(registry.byNode(this), evt);
evt.stopPropagation();
}),
on(this.containerNode, on.selector(matches, "focusin"), function(){
self._onItemFocus(registry.byNode(this));
})
);
this.inherited(arguments);
},
onKeyboardSearch: function(/*MenuItem*/ item, /*Event*/ evt, /*String*/ searchString, /*Number*/ numMatches){
// summary:
// Attach point for notification about when a menu item has been searched for
// via the keyboard search mechanism.
// tags:
// protected
this.inherited(arguments);
if(!!item && (numMatches == -1 || (!!item.popup && numMatches == 1))){
this.onItemClick(item, evt);
}
},
_keyboardSearchCompare: function(/*dijit/_WidgetBase*/ item, /*String*/ searchString){
// summary:
// Compares the searchString to the widget's text label, returning:
// -1: a high priority match and stop searching
// 0: no match
// 1: a match but keep looking for a higher priority match
// tags:
// private
if(!!item.shortcutKey){
// accessKey matches have priority
return searchString == item.shortcutKey.toLowerCase() ? -1 : 0;
}
return this.inherited(arguments) ? 1 : 0; // change return value of -1 to 1 so that searching continues
},
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.onItemClick(this.focusedChild, evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
_onPopupHover: function(/*Event*/ /*===== evt =====*/){
// summary:
// This handler is called when the mouse moves over the popup.
// tags:
// private
// if the mouse hovers over a menu popup that is in pending-close state,
// then stop the close operation.
// This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
// highlight the parent menu item pointing to this popup (in case user temporarily moused over another MenuItem)
this.set("selected", this.currentPopupItem);
// cancel the pending close (if there is one) (in case user temporarily moused over another MenuItem)
this._stopPendingCloseTimer();
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) opening it from a parent menu (which automatically activates it)
if(this.activated){
this.set("selected", item);
if(item.popup && !item.disabled && !this.hover_timer){
this.hover_timer = this.defer(function(){
this._openItemPopup(item);
}, this.popupDelay);
}
}else if(this.passivePopupDelay < Infinity){
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.passive_hover_timer = this.defer(function(){
this.onItemClick(item, {type: "click"});
}, this.passivePopupDelay);
}
this._hoveredChild = item;
item._set("hovering", true);
},
_onChildDeselect: function(item){
// summary:
// Called when a child MenuItem becomes deselected. Setup timer to close its popup.
this._stopPopupTimer();
// Setup timer to close all popups that are open and descendants of this menu.
// Will be canceled if user quickly moves the mouse over the popup.
if(this.currentPopupItem == item){
this._stopPendingCloseTimer();
this._pendingClose_timer = this.defer(function(){
this._pendingClose_timer = null;
this.currentPopupItem = null;
item._closePopup(); // this calls onClose
}, this.popupDelay);
}
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this._hoveredChild == item){
this._hoveredChild = null;
}
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
this.passive_hover_timer = null;
}
item._set("hovering", false);
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
this.hover_timer = this.hover_timer.remove();
}
},
_stopPendingCloseTimer: function(){
// summary:
// Cancels the pending-close timer because the close has been preempted
// tags:
// private
if(this._pendingClose_timer){
this._pendingClose_timer = this._pendingClose_timer.remove();
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top = this; top.parentMenu; top = top.parentMenu){}
return top;
},
onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
if(this.passive_hover_timer){
this.passive_hover_timer.remove();
}
this.focusChild(item);
if(item.disabled){
return false;
}
if(item.popup){
this.set("selected", item);
this.set("activated", true);
var byKeyboard = /^key/.test(evt._origType || evt.type) ||
(evt.clientX == 0 && evt.clientY == 0); // detects accessKey like ALT+SHIFT+F, where type is "click"
this._openItemPopup(item, byKeyboard);
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item._onClick ? item._onClick(evt) : item.onClick(evt);
}
},
_openItemPopup: function(/*dijit/MenuItem*/ from_item, /*Boolean*/ focus){
// summary:
// Open the popup to the side of/underneath the current menu item, and optionally focus first item
// tags:
// protected
if(from_item == this.currentPopupItem){
// Specified popup is already being shown, so just return
return;
}
if(this.currentPopupItem){
// If another popup is currently shown, then close it
this._stopPendingCloseTimer();
this.currentPopupItem._closePopup();
}
this._stopPopupTimer();
var popup = from_item.popup;
popup.parentMenu = this;
// detect mouseover of the popup to handle lazy mouse movements that temporarily focus other menu items\c
this.own(this._mouseoverHandle = on.once(popup.domNode, "mouseover", lang.hitch(this, "_onPopupHover")));
var self = this;
from_item._openPopup({
parent: this,
orient: this._orient || ["after", "before"],
onCancel: function(){ // called when the child menu is canceled
if(focus){
// put focus back on my node before focused node is hidden
self.focusChild(from_item);
}
// close the submenu (be sure this is done _after_ focus is moved)
self._cleanUp();
},
onExecute: lang.hitch(this, "_cleanUp", true),
onClose: function(){
// Remove handler created by onItemHover
if(self._mouseoverHandle){
self._mouseoverHandle.remove();
delete self._mouseoverHandle;
}
}
}, focus);
this.currentPopupItem = from_item;
// TODO: focusing a popup should clear tabIndex on Menu (and it's child MenuItems), so that neither
// TAB nor SHIFT-TAB returns to the menu. Only ESC or ENTER should return to the menu.
},
onOpen: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback when this menu is opened.
// This is called by the popup manager as notification that the menu
// was opened.
// tags:
// private
this.isShowingNow = true;
this.set("activated", true);
},
onClose: function(){
// summary:
// Callback when this menu is closed.
// This is called by the popup manager as notification that the menu
// was closed.
// tags:
// private
this.set("activated", false);
this.set("selected", null);
this.isShowingNow = false;
this.parentMenu = null;
},
_closeChild: function(){
// summary:
// Called when submenu is clicked or focus is lost. Close hierarchy of menus.
// tags:
// private
this._stopPopupTimer();
if(this.currentPopupItem){
// If focus is on a descendant MenuItem then move focus to me,
// because IE doesn't like it when you display:none a node with focus,
// and also so keyboard users don't lose control.
// Likely, immediately after a user defined onClick handler will move focus somewhere
// else, like a Dialog.
if(this.focused){
domAttr.set(this.selected.focusNode, "tabIndex", this.tabIndex);
this.selected.focusNode.focus();
}
// Close all popups that are open and descendants of this menu
this.currentPopupItem._closePopup();
this.currentPopupItem = null;
}
},
_onItemFocus: function(/*MenuItem*/ item){
// summary:
// Called when child of this Menu gets focus from:
//
// 1. clicking it
// 2. tabbing into it
// 3. being opened by a parent menu.
//
// This is not called just from mouse hover.
if(this._hoveredChild && this._hoveredChild != item){
this.onItemUnhover(this._hoveredChild); // any previous mouse movement is trumped by focus selection
}
this.set("selected", item);
},
_onBlur: function(){
// summary:
// Called when focus is moved away from this Menu and it's submenus.
// tags:
// protected
this._cleanUp(true);
this.inherited(arguments);
},
_cleanUp: function(/*Boolean*/ clearSelectedItem){
// summary:
// Called when the user is done with this menu. Closes hierarchy of menus.
// tags:
// private
this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
this.set("activated", false);
}
if(clearSelectedItem){
this.set("selected", null);
}
}
});
});
@@ -1,8 +1,31 @@
//>>built
define("dijit/_OnDijitClickMixin",["dojo/on","dojo/_base/array","dojo/keys","dojo/_base/declare","dojo/has","./a11yclick"],function(on,_1,_2,_3,_4,_5){
var _6=_3("dijit._OnDijitClickMixin",null,{connect:function(_7,_8,_9){
return this.inherited(arguments,[_7,_8=="ondijitclick"?_5:_8,_9]);
}});
_6.a11yclick=_5;
return _6;
define([
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"./a11yclick"
], function(on, array, keys, declare, has, a11yclick){
// module:
// dijit/_OnDijitClickMixin
var ret = declare("dijit._OnDijitClickMixin", null, {
// summary:
// Deprecated. New code should access the dijit/a11yclick event directly, ex:
// | this.own(on(node, a11yclick, function(){ ... }));
//
// Mixing in this class will make _WidgetBase.connect(node, "ondijitclick", ...) work.
// It also used to be necessary to make templates with ondijitclick work, but now you can just require
// dijit/a11yclick.
connect: function(obj, event, method){
// override _WidgetBase.connect() to make this.connect(node, "ondijitclick", ...) work
return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
}
});
ret.a11yclick = a11yclick; // back compat
return ret;
});
@@ -1,31 +0,0 @@
define("dijit/_OnDijitClickMixin", [
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"./a11yclick"
], function(on, array, keys, declare, has, a11yclick){
// module:
// dijit/_OnDijitClickMixin
var ret = declare("dijit._OnDijitClickMixin", null, {
// summary:
// Deprecated. New code should access the dijit/a11yclick event directly, ex:
// | this.own(on(node, a11yclick, function(){ ... }));
//
// Mixing in this class will make _WidgetBase.connect(node, "ondijitclick", ...) work.
// It also used to be necessary to make templates with ondijitclick work, but now you can just require
// dijit/a11yclick.
connect: function(obj, event, method){
// override _WidgetBase.connect() to make this.connect(node, "ondijitclick", ...) work
return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
}
});
ret.a11yclick = a11yclick; // back compat
return ret;
});

Some files were not shown because too many files have changed in this diff Show More