FilePickerTextBox.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. require({cache:{
  2. 'url:dojox/form/resources/FilePickerTextBox.html':"<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\" tabIndex=\"-1\"\n\t><div style=\"overflow:hidden;\"\n\t\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton'\n\t\t\tdojoAttachPoint=\"downArrowNode,_buttonNode,_popupStateNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitArrowButtonInner\">&thinsp;</div\n\t\t\t><div class=\"dijitArrowButtonChar\">&#9660;</div\n\t\t></div\n\t\t><div class=\"dijitReset dijitValidationIcon\"><br></div\n\t\t><div class=\"dijitReset dijitValidationIconText\">&Chi;</div\n\t\t><div class=\"dijitReset dijitInputField\"\n\t\t\t><input type=\"text\" autocomplete=\"off\" ${!nameAttrSetting} class='dijitReset'\n\t\t\t\tdojoAttachEvent='onkeypress:_onKey' \n\t\t\t\tdojoAttachPoint='textbox,focusNode' role=\"textbox\" aria-haspopup=\"true\" aria-autocomplete=\"list\"\n\t\t/></div\n\t></div\n></div>\n"}});
  3. define("dojox/form/FilePickerTextBox", [
  4. "dojo/_base/lang",
  5. "dojo/_base/array",
  6. "dojo/_base/event",
  7. "dojo/window",
  8. "dijit/focus",
  9. "dijit/registry",
  10. "dijit/form/_TextBoxMixin",
  11. "dijit/form/ValidationTextBox",
  12. "dijit/_HasDropDown",
  13. "dojox/widget/FilePicker",
  14. "dojo/text!./resources/FilePickerTextBox.html",
  15. "dojo/_base/declare",
  16. "dojo/keys" // keys
  17. ], function(lang, array, event, windowUtils, focus, registry, _TextBoxMixin, ValidationTextBox, _HasDropDown, FilePicker, template, declare, keys){
  18. /*=====
  19. ValidationTextBox = dijit.form.ValidationTextBox;
  20. _HasDropDown = dijit._HasDropDown;
  21. =====*/
  22. return declare( "dojox.form.FilePickerTextBox", [ValidationTextBox, _HasDropDown],
  23. {
  24. // summary:
  25. // A validating text box tied to a file picker popup
  26. baseClass: "dojoxFilePickerTextBox",
  27. templateString: template,
  28. // searchDelay: Integer
  29. // Delay in milliseconds between when user types something and we start
  30. // searching based on that value
  31. searchDelay: 500,
  32. // valueItem: item
  33. // The item, in our store, of the directory relating to our value
  34. valueItem: null,
  35. // numPanes: number
  36. // The number of panes to display in our box (if we don't have any
  37. // minPaneWidth specified by our constraints)
  38. numPanes: 2.25,
  39. postMixInProperties: function(){
  40. this.inherited(arguments);
  41. this.dropDown = new FilePicker(this.constraints);
  42. },
  43. postCreate: function(){
  44. this.inherited(arguments);
  45. // Make our connections we want
  46. this.connect(this.dropDown, "onChange", this._onWidgetChange);
  47. this.connect(this.focusNode, "onblur", "_focusBlur");
  48. this.connect(this.focusNode, "onfocus", "_focusFocus");
  49. this.connect(this.focusNode, "ondblclick", function(){
  50. _TextBoxMixin.selectInputText(this.focusNode);
  51. });
  52. },
  53. _setValueAttr: function(/*string*/value, priorityChange, fromWidget){
  54. // summary: sets the value of this widget
  55. if(!this._searchInProgress){
  56. this.inherited(arguments);
  57. value = value || "";
  58. var tVal = this.dropDown.get("pathValue") || "";
  59. if(value !== tVal){
  60. this._skip = true;
  61. var fx = lang.hitch(this, "_setBlurValue");
  62. this.dropDown._setPathValueAttr(value, !fromWidget,
  63. this._settingBlurValue ? fx : null);
  64. }
  65. }
  66. },
  67. _onWidgetChange: function(/*item*/item){
  68. // summary: called when the path gets changed in the dropdown
  69. if(!item && this.focusNode.value){
  70. this._hasValidPath = false;
  71. this.focusNode.value = "";
  72. }else{
  73. this.valueItem = item;
  74. var value = this.dropDown._getPathValueAttr(item);
  75. if(value){
  76. this._hasValidPath = true;
  77. }
  78. if(!this._skip){
  79. this._setValueAttr(value, undefined, true);
  80. }
  81. delete this._skip;
  82. }
  83. this.validate();
  84. },
  85. startup: function(){
  86. if(!this.dropDown._started){
  87. this.dropDown.startup();
  88. }
  89. this.inherited(arguments);
  90. },
  91. openDropDown: function(){
  92. // set width to 0 so that it will resize automatically
  93. this.dropDown.domNode.style.width="0px";
  94. if(!("minPaneWidth" in (this.constraints||{}))){
  95. this.dropDown.set("minPaneWidth", (this.domNode.offsetWidth / this.numPanes));
  96. }
  97. this.inherited(arguments);
  98. },
  99. toggleDropDown: function(){
  100. this.inherited(arguments);
  101. // Make sure our display is up-to-date with our value
  102. if(this._opened){
  103. this.dropDown.set("pathValue", this.get("value"));
  104. }
  105. },
  106. _focusBlur: function(/*Event*/ e){
  107. // summary: called when the focus node gets blurred
  108. if(e.explicitOriginalTarget == this.focusNode && !this._allowBlur){
  109. window.setTimeout(lang.hitch(this, function(){
  110. if(!this._allowBlur){
  111. this.focus();
  112. }
  113. }), 1);
  114. }else if(this._menuFocus){
  115. this.dropDown._updateClass(this._menuFocus, "Item", {"Hover": false});
  116. delete this._menuFocus;
  117. }
  118. },
  119. _focusFocus: function(/*Event*/ e){
  120. // summary: called when the focus node gets focus
  121. if(this._menuFocus){
  122. this.dropDown._updateClass(this._menuFocus, "Item", {"Hover": false});
  123. }
  124. delete this._menuFocus;
  125. var focusNode = focus.curNode;
  126. if(focusNode){
  127. focusNode = registry.byNode(focusNode);
  128. if(focusNode){
  129. this._menuFocus = focusNode.domNode;
  130. }
  131. }
  132. if(this._menuFocus){
  133. this.dropDown._updateClass(this._menuFocus, "Item", {"Hover": true});
  134. }
  135. delete this._allowBlur;
  136. },
  137. _onBlur: function(){
  138. // summary: called when focus is shifted away from this widget
  139. this._allowBlur = true;
  140. delete this.dropDown._savedFocus;
  141. this.inherited(arguments);
  142. },
  143. _setBlurValue: function(){
  144. // summary: sets the value of the widget once focus has left
  145. if(this.dropDown && !this._settingBlurValue){
  146. this._settingBlurValue = true;
  147. this.set("value", this.focusNode.value);
  148. }else{
  149. delete this._settingBlurValue;
  150. this.inherited(arguments);
  151. }
  152. },
  153. parse: function(/* String */ value, /* Object */ constraints){
  154. // summary:
  155. // Function to convert a formatted string to a value - we use
  156. // it to verify that it *really* is a valid value
  157. if(this._hasValidPath || this._hasSelection){
  158. return value;
  159. }
  160. var dd = this.dropDown, topDir = dd.topDir, sep = dd.pathSeparator;
  161. var ddVal = dd.get("pathValue");
  162. var norm = function(v){
  163. if(topDir.length && v.indexOf(topDir) === 0){
  164. v = v.substring(topDir.length);
  165. }
  166. if(sep && v[v.length - 1] == sep){
  167. v = v.substring(0, v.length - 1);
  168. }
  169. return v;
  170. };
  171. ddVal = norm(ddVal);
  172. var val = norm(value);
  173. if(val == ddVal){
  174. return value;
  175. }
  176. return undefined;
  177. },
  178. _startSearchFromInput: function(){
  179. // summary: kicks off a search based off the current text value of the widget
  180. var dd = this.dropDown, fn = this.focusNode;
  181. var val = fn.value, oVal = val, topDir = dd.topDir;
  182. if(this._hasSelection){
  183. _TextBoxMixin.selectInputText(fn, oVal.length);
  184. }
  185. this._hasSelection = false;
  186. if(topDir.length && val.indexOf(topDir) === 0){
  187. val = val.substring(topDir.length);
  188. }
  189. var dirs = val.split(dd.pathSeparator);
  190. var setFromChain = lang.hitch(this, function(idx){
  191. var dir = dirs[idx];
  192. var child = dd.getChildren()[idx];
  193. var conn;
  194. this._searchInProgress = true;
  195. var _cleanup = lang.hitch(this, function(){
  196. delete this._searchInProgress;
  197. });
  198. if((dir || child) && !this._opened){
  199. this.toggleDropDown();
  200. }
  201. if(dir && child){
  202. var fx = lang.hitch(this, function(){
  203. if(conn){
  204. this.disconnect(conn);
  205. }
  206. delete conn;
  207. var children = child._menu.getChildren();
  208. var exact = array.filter(children, function(i){
  209. return i.label == dir;
  210. })[0];
  211. var first = array.filter(children, function(i){
  212. return (i.label.indexOf(dir) === 0);
  213. })[0];
  214. if(exact &&
  215. ((dirs.length > idx + 1 && exact.children) ||
  216. (!exact.children))){
  217. idx++;
  218. child._menu.onItemClick(exact, {type: "internal",
  219. stopPropagation: function(){},
  220. preventDefault: function(){}});
  221. if(dirs[idx]){
  222. setFromChain(idx);
  223. }else{
  224. _cleanup();
  225. }
  226. }else{
  227. child._setSelected(null);
  228. if(first && dirs.length === idx + 1){
  229. dd._setInProgress = true;
  230. dd._removeAfter(child);
  231. delete dd._setInProgress;
  232. var targetString = first.label;
  233. if(first.children){
  234. targetString += dd.pathSeparator;
  235. }
  236. targetString = targetString.substring(dir.length);
  237. window.setTimeout(function(){
  238. windowUtils.scrollIntoView(first.domNode);
  239. }, 1);
  240. fn.value = oVal + targetString;
  241. _TextBoxMixin.selectInputText(fn, oVal.length);
  242. this._hasSelection = true;
  243. try{first.focusNode.focus();}catch(e){}
  244. }else{
  245. if(this._menuFocus){
  246. this.dropDown._updateClass(this._menuFocus, "Item", {"Hover": false, "Focus": false});
  247. }
  248. delete this._menuFocus;
  249. }
  250. _cleanup();
  251. }
  252. });
  253. if(!child.isLoaded){
  254. conn = this.connect(child, "onLoad", fx);
  255. }else{
  256. fx();
  257. }
  258. }else{
  259. if(child){
  260. child._setSelected(null);
  261. dd._setInProgress = true;
  262. dd._removeAfter(child);
  263. delete dd._setInProgress;
  264. }
  265. _cleanup();
  266. }
  267. });
  268. setFromChain(0);
  269. },
  270. _onKey: function(/*Event*/ e){
  271. // summary: callback when the user presses a key on menu popup node
  272. if(this.disabled || this.readOnly){ return; }
  273. var c = e.charOrCode;
  274. if(c==keys.DOWN_ARROW){
  275. this._allowBlur = true;
  276. }
  277. if(c==keys.ENTER && this._opened){
  278. this.dropDown.onExecute();
  279. _TextBoxMixin.selectInputText(this.focusNode, this.focusNode.value.length);
  280. this._hasSelection = false;
  281. event.stop(e);
  282. return;
  283. }
  284. if((c==keys.RIGHT_ARROW || c==keys.LEFT_ARROW || c==keys.TAB) && this._hasSelection){
  285. this._startSearchFromInput();
  286. event.stop(e);
  287. return;
  288. }
  289. this.inherited(arguments);
  290. var doSearch = false;
  291. if((c==keys.BACKSPACE || c==keys.DELETE) && this._hasSelection){
  292. this._hasSelection = false;
  293. }else if(c==keys.BACKSPACE || c==keys.DELETE || c==" "){
  294. doSearch = true;
  295. }else{
  296. doSearch = e.keyChar !== "";
  297. }
  298. if(this._searchTimer){
  299. window.clearTimeout(this._searchTimer);
  300. }
  301. delete this._searchTimer;
  302. if(doSearch){
  303. this._hasValidPath = false;
  304. this._hasSelection = false;
  305. this._searchTimer = window.setTimeout(lang.hitch(this, "_startSearchFromInput"), this.searchDelay + 1);
  306. }
  307. }
  308. }
  309. );
  310. });