FilePickerTextBox.js 10 KB

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