ListInput.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  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.ListInput"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.form.ListInput"] = true;
  8. dojo.experimental("dojox.form.ListInput");
  9. dojo.provide("dojox.form.ListInput");
  10. dojo.require("dijit.form._FormWidget");
  11. dojo.require("dijit.form.ValidationTextBox");
  12. dojo.require("dijit.InlineEditBox");
  13. dojo.requireLocalization("dijit", "common", null, "ROOT,ar,az,bg,ca,cs,da,de,el,es,fi,fr,he,hr,hu,it,ja,kk,ko,nb,nl,pl,pt,pt-pt,ro,ru,sk,sl,sv,th,tr,zh,zh-tw");
  14. dojo.declare("dojox.form.ListInput",
  15. [dijit.form._FormValueWidget],
  16. {
  17. // summary:
  18. // An automatic list maker
  19. // description:
  20. // you can add value to list with add method.
  21. // you can only remove by clicking close button
  22. constructor: function(){
  23. this._items = [];
  24. if(!dojo.isArray(this.delimiter)){
  25. this.delimiter=[this.delimiter];
  26. }
  27. var r="("+this.delimiter.join("|")+")?";
  28. this.regExp="^"+this.regExp+r+"$";
  29. },
  30. // inputClass: String
  31. // Class which will be used to create the input box. You can implements yours.
  32. // It must be a widget, focusNode or domNode must have "onkeydown" event
  33. // It must have .attr("value") to get value
  34. // It also must impement an (or more) handler for the "onChange" method
  35. inputClass: "dojox.form._ListInputInputBox",
  36. // inputHandler: String || Array
  37. // The widget will connect on all handler to check input value
  38. // You can use comma separated list
  39. inputHandler: "onChange",
  40. // inputProperties: String || Object
  41. // Properties used to create input box
  42. // If String, it must be a valid JSON
  43. inputProperties: {
  44. minWidth:50
  45. },
  46. // submitOnlyValidValue: Boolean
  47. // If true, only valid value will be submited with form
  48. submitOnlyValidValue:true,
  49. // useOnBlur: Boolean
  50. // If true, onBlur event do a validate (like pressing ENTER)
  51. useOnBlur:true,
  52. // readOnlyInput: Boolean
  53. // if false, the list will be editable
  54. // Can only be set when instanciate
  55. readOnlyInput: false,
  56. // maxItems: Int
  57. // Specify max item the list can have
  58. // null = infiny
  59. maxItems: null,
  60. // showCloseButtonWhenValid: Boolean
  61. // if true, a close button will be added on valid item
  62. showCloseButtonWhenValid: true,
  63. // showCloseButtonWhenInvalid: Boolean
  64. // if true, a close button will be added on invalid item
  65. showCloseButtonWhenInvalid: true,
  66. // regExp: [extension protected] String
  67. // regular expression string used to validate the input
  68. // Do not specify both regExp and regExpGen
  69. regExp: ".*", //"[a-zA-Z.-_]+@[a-zA-Z.-_]+.[a-zA-Z]+",
  70. // delimiter: String || Array
  71. // delimiter for the string. Every match will be splitted
  72. // The string can contain only one delimiter
  73. delimiter: ",",
  74. // constraints: dijit.form.ValidationTextBox.__Constraints
  75. // user-defined object needed to pass parameters to the validator functions
  76. constraints: {},
  77. baseClass:"dojoxListInput",
  78. type: "select",
  79. value: "",
  80. templateString: "<div dojoAttachPoint=\"focusNode\" class=\"dijit dijitReset dijitLeft dojoxListInput\"><select dojoAttachpoint=\"_selectNode\" multiple=\"multiple\" class=\"dijitHidden\" ${!nameAttrSetting}></select><ul dojoAttachPoint=\"_listInput\"><li dojoAttachEvent=\"onclick: _onClick\" class=\"dijitInputField dojoxListInputNode dijitHidden\" dojoAttachPoint=\"_inputNode\"></li></ul></div>",
  81. // useAnim: Boolean
  82. // If true, then item will use an anime to show hide itself
  83. useAnim: true,
  84. // duration: Integer
  85. // Animation duration
  86. duration: 500,
  87. // easingIn: function
  88. // function used to easing on fadeIn end
  89. easingIn: null,
  90. // easingOut: function
  91. // function used to easing on fadeOut end
  92. easingOut: null,
  93. // readOnlyItem: Boolean
  94. // If true, items can be edited
  95. // Can only be set when instanciate
  96. readOnlyItem: false,
  97. // useArrowForEdit: Boolean
  98. // If true, arraow left and right can be used for editing
  99. // Can only be set when instanciate
  100. useArrowForEdit: true,
  101. // _items: Array
  102. // Array of widget.
  103. // Contain all reference to _ListInputInputItem
  104. _items: null,
  105. // _lastAddedItem: Widget
  106. // Contain a reference to the last created item
  107. _lastAddedItem: null,
  108. // _currentItem: Widget
  109. // Widget currently in edition
  110. _currentItem: null,
  111. // _input: Widget
  112. // Widget use for input box
  113. _input: null,
  114. // _count: Int
  115. // Count items
  116. _count: 0,
  117. postCreate: function(){
  118. // summary:
  119. // If closeButton is used, add a class
  120. this.inherited(arguments);
  121. this._createInputBox();
  122. },
  123. _setReadOnlyInputAttr: function(/*Boolean*/value){
  124. // summary:
  125. // Change status and if needed, create the inputbox
  126. // tags:
  127. // private
  128. if(!this._started){ return this._createInputBox(); }
  129. this.readOnlyInput = value;
  130. this._createInputBox();
  131. },
  132. _setReadOnlyItemAttr: function(/*Boolean*/value){
  133. // summary:
  134. // set read only items
  135. // tags:
  136. // private
  137. if(!this._started){ return; }
  138. for(var i in this._items){
  139. this._items[i].set("readOnlyItem", value);
  140. }
  141. },
  142. _createInputBox: function(){
  143. // summary:
  144. // Create the input box
  145. // tags:
  146. // private
  147. dojo.toggleClass(this._inputNode, "dijitHidden", this.readOnlyInput);
  148. if(this.readOnlyInput){ return; }
  149. if(this._input){ return; }
  150. if(this.inputHandler === null){
  151. console.warn("you must add some handler to connect to input field");
  152. return false;
  153. }
  154. if(dojo.isString(this.inputHandler)){
  155. this.inputHandler = this.inputHandler.split(",");
  156. }
  157. if(dojo.isString(this.inputProperties)){
  158. this.inputProperties = dojo.fromJson(this.inputProperties);
  159. }
  160. var input = dojo.getObject(this.inputClass, false);
  161. this.inputProperties.regExp = this.regExpGen(this.constraints);
  162. this._input = new input(this.inputProperties);
  163. this._input.startup();
  164. this._inputNode.appendChild(this._input.domNode);
  165. dojo.forEach(this.inputHandler, function(handler){
  166. this.connect(this._input,dojo.string.trim(handler),"_onHandler");
  167. },this);
  168. this.connect(this._input, "onKeyDown", "_inputOnKeyDown");
  169. this.connect(this._input, "onBlur", "_inputOnBlur");
  170. },
  171. compare: function(/*Array*/val1,/*Array*/val2){
  172. // summary:
  173. // Compare 2 values (as returned by attr('value') for this widget).
  174. // tags:
  175. // protected
  176. val1 = val1.join(",");
  177. val2 = val2.join(",");
  178. if(val1 > val2){
  179. return 1;
  180. }else if(val1 < val2){
  181. return -1;
  182. }else{
  183. return 0;
  184. }
  185. },
  186. add: function(/*String || Array*/values){
  187. // summary:
  188. // Create new list element
  189. if(this._count>=this.maxItems && this.maxItems !== null){return;}
  190. this._lastValueReported = this._getValues();
  191. if(!dojo.isArray(values)){
  192. values = [values];
  193. }
  194. for(var i in values){
  195. var value=values[i];
  196. if(value === "" || typeof value != "string"){
  197. continue;
  198. }
  199. this._count++;
  200. var re = new RegExp(this.regExpGen(this.constraints));
  201. this._lastAddedItem = new dojox.form._ListInputInputItem({
  202. "index" : this._items.length,
  203. readOnlyItem : this.readOnlyItem,
  204. value : value,
  205. regExp: this.regExpGen(this.constraints)
  206. });
  207. this._lastAddedItem.startup();
  208. this._testItem(this._lastAddedItem,value);
  209. this._lastAddedItem.onClose = dojo.hitch(this,"_onItemClose",this._lastAddedItem);
  210. this._lastAddedItem.onChange = dojo.hitch(this,"_onItemChange",this._lastAddedItem);
  211. this._lastAddedItem.onEdit = dojo.hitch(this,"_onItemEdit",this._lastAddedItem);
  212. this._lastAddedItem.onKeyDown = dojo.hitch(this,"_onItemKeyDown",this._lastAddedItem);
  213. if(this.useAnim){
  214. dojo.style(this._lastAddedItem.domNode, {opacity:0, display:""});
  215. }
  216. this._placeItem(this._lastAddedItem.domNode);
  217. if(this.useAnim){
  218. var anim = dojo.fadeIn({
  219. node : this._lastAddedItem.domNode,
  220. duration : this.duration,
  221. easing : this.easingIn
  222. }).play();
  223. }
  224. this._items[this._lastAddedItem.index] = this._lastAddedItem;
  225. if(this._onChangeActive && this.intermediateChanges){ this.onChange(value); }
  226. if(this._count>=this.maxItems && this.maxItems !== null){
  227. break;
  228. }
  229. }
  230. this._updateValues();
  231. if(this._lastValueReported.length==0){
  232. this._lastValueReported = this.value;
  233. }
  234. if(!this.readOnlyInput){
  235. this._input.set("value", "");
  236. }
  237. if(this._onChangeActive){ this.onChange(this.value); }
  238. this._setReadOnlyWhenMaxItemsReached();
  239. },
  240. _setReadOnlyWhenMaxItemsReached: function(){
  241. // summary:
  242. // set input to readonly when max is reached
  243. // tags:
  244. // private
  245. this.set("readOnlyInput",(this._count>=this.maxItems && this.maxItems !== null));
  246. },
  247. _setSelectNode: function(){
  248. // summary:
  249. // put all item in the select (for a submit)
  250. // tags:
  251. // private
  252. this._selectNode.options.length = 0;
  253. var values=this.submitOnlyValidValue?this.get("MatchedValue"):this.value;
  254. if(!dojo.isArray(values)){
  255. return;
  256. }
  257. dojo.forEach(values,function(item){
  258. this._selectNode.options[this._selectNode.options.length]=new Option(item,item,true,true);
  259. },this);
  260. },
  261. _placeItem: function(/*domNode*/node){
  262. // summary:
  263. // Place item in the list
  264. // tags:
  265. // private
  266. dojo.place(node,this._inputNode,"before");
  267. },
  268. _getCursorPos: function(/*domNode*/node){
  269. // summary:
  270. // get current cursor pos
  271. // tags:
  272. // private
  273. if(typeof node.selectionStart != 'undefined'){
  274. return node.selectionStart;
  275. }
  276. // IE Support
  277. try{ node.focus(); }catch(e){}
  278. var range = node.createTextRange();
  279. range.moveToBookmark(dojo.doc.selection.createRange().getBookmark());
  280. range.moveEnd('character', node.value.length);
  281. try{
  282. return node.value.length - range.text.length;
  283. }finally{ range=null; }
  284. },
  285. _onItemClose: function(/*dijit._Widget*/ item){
  286. // summary:
  287. // Destroy a list element when close button is clicked
  288. // tags:
  289. // private
  290. if(this.disabled){ return; }
  291. if(this.useAnim){
  292. var anim = dojo.fadeOut({
  293. node : item.domNode,
  294. duration : this.duration,
  295. easing : this.easingOut,
  296. onEnd : dojo.hitch(this, "_destroyItem", item)
  297. }).play();
  298. }else{
  299. this._destroyItem(item);
  300. }
  301. },
  302. _onItemKeyDown: function(/*dijit._Widget*/ item, /*Event*/ e){
  303. // summary:
  304. // Call when item get a keypress
  305. // tags:
  306. // private
  307. if(this.readOnlyItem || !this.useArrowForEdit){ return; }
  308. if(e.keyCode == dojo.keys.LEFT_ARROW && this._getCursorPos(e.target)==0){
  309. this._editBefore(item);
  310. }else if(e.keyCode == dojo.keys.RIGHT_ARROW && this._getCursorPos(e.target)==e.target.value.length){
  311. this._editAfter(item);
  312. }
  313. },
  314. _editBefore: function(/*widget*/item) {
  315. // summary:
  316. // move trough items
  317. // tags:
  318. // private
  319. this._currentItem = this._getPreviousItem(item);
  320. if(this._currentItem !== null){
  321. this._currentItem.edit();
  322. }
  323. },
  324. _editAfter: function(/*widget*/item) {
  325. // summary:
  326. // move trough items
  327. // tags:
  328. // private
  329. this._currentItem = this._getNextItem(item);
  330. if(this._currentItem !== null){
  331. this._currentItem.edit();
  332. }
  333. if(!this.readOnlyInput){
  334. if(this._currentItem === null){
  335. //no more item ?
  336. //so edit input (if available)
  337. this._focusInput();
  338. }
  339. }
  340. },
  341. _onItemChange: function(/*dijit._Widget*/ item, /*String*/ value){
  342. // summary:
  343. // Call when item value change
  344. // tags:
  345. // private
  346. value = value || item.get("value");
  347. //revalidate content
  348. this._testItem(item,value);
  349. //update value
  350. this._updateValues();
  351. },
  352. _onItemEdit: function(/*dijit._Widget*/ item){
  353. // summary:
  354. // Call when item is edited
  355. // tags:
  356. // private
  357. dojo.removeClass(item.domNode,["dijitError", this.baseClass + "Match", this.baseClass + "Mismatch"]);
  358. },
  359. _testItem: function(/*Object*/item,/*String*/value){
  360. // summary:
  361. // Change class of item (match, mismatch)
  362. // tags:
  363. // private
  364. var re = new RegExp(this.regExpGen(this.constraints));
  365. var match = value.match(re);
  366. dojo.removeClass(item.domNode, this.baseClass + (!match ? "Match" : "Mismatch"));
  367. dojo.addClass(item.domNode, this.baseClass + (match ? "Match" : "Mismatch"));
  368. dojo.toggleClass(item.domNode, "dijitError", !match);
  369. if((this.showCloseButtonWhenValid && match) ||
  370. (this.showCloseButtonWhenInvalid && !match)){
  371. dojo.addClass(item.domNode,this.baseClass+"Closable");
  372. }else {
  373. dojo.removeClass(item.domNode,this.baseClass+"Closable");
  374. }
  375. },
  376. _getValueAttr: function(){
  377. // summary:
  378. // get all value in then list and return an array
  379. // tags:
  380. // private
  381. return this.value;
  382. },
  383. _setValueAttr: function(/*Array || String*/ newValue){
  384. // summary:
  385. // Hook so attr('value', value) works.
  386. // description:
  387. // Sets the value of the widget.
  388. // If the value has changed, then fire onChange event, unless priorityChange
  389. // is specified as null (or false?)
  390. this._destroyAllItems();
  391. this.add(this._parseValue(newValue));
  392. },
  393. _parseValue: function(/*String*/newValue){
  394. // summary:
  395. // search for delemiters and split if needed
  396. // tags:
  397. // private
  398. if(typeof newValue == "string"){
  399. if(dojo.isString(this.delimiter)){
  400. this.delimiter = [this.delimiter];
  401. }
  402. var re = new RegExp("^.*("+this.delimiter.join("|")+").*");
  403. if(newValue.match(re)){
  404. re = new RegExp(this.delimiter.join("|"));
  405. return newValue.split(re);
  406. }
  407. }
  408. return newValue;
  409. },
  410. regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){
  411. // summary:
  412. // Overridable function used to generate regExp when dependent on constraints.
  413. // Do not specify both regExp and regExpGen.
  414. // tags:
  415. // extension protected
  416. return this.regExp; // String
  417. },
  418. _setDisabledAttr: function(/*Boolean*/ value){
  419. // summary:
  420. // also enable/disable editable items
  421. // tags:
  422. // private
  423. if(!this.readOnlyItem){
  424. for(var i in this._items){
  425. this._items[i].set("disabled", value);
  426. }
  427. }
  428. if(!this.readOnlyInput){
  429. this._input.set("disabled", value);
  430. }
  431. this.inherited(arguments);
  432. },
  433. _onHandler: function(/*String*/value){
  434. // summary:
  435. // When handlers of input are fired, this method check input value and (if needed) modify it
  436. // tags:
  437. // private
  438. var parsedValue = this._parseValue(value);
  439. if(dojo.isArray(parsedValue)){
  440. this.add(parsedValue);
  441. }
  442. },
  443. _onClick: function(/*event*/e){
  444. // summary:
  445. // give focus to inputbox
  446. // tags:
  447. // private
  448. this._focusInput();
  449. },
  450. _focusInput: function(){
  451. // summary:
  452. // give focus to input
  453. // tags:
  454. // private
  455. if(!this.readOnlyInput && this._input.focus){
  456. this._input.focus();
  457. }
  458. },
  459. _inputOnKeyDown: function(/*event*/e){
  460. // summary:
  461. // Used to add keybord interactivity
  462. // tags:
  463. // private
  464. this._currentItem = null;
  465. var val = this._input.get("value");
  466. if(e.keyCode == dojo.keys.BACKSPACE && val == "" && this.get("lastItem")){
  467. this._destroyItem(this.get("lastItem"));
  468. }else if(e.keyCode == dojo.keys.ENTER && val != ""){
  469. this.add(val);
  470. }else if(e.keyCode == dojo.keys.LEFT_ARROW && this._getCursorPos(this._input.focusNode) == 0 &&
  471. !this.readOnlyItem && this.useArrowForEdit){
  472. this._editBefore();
  473. }
  474. },
  475. _inputOnBlur: function(){
  476. // summary:
  477. // Remove focus class and act like pressing ENTER key
  478. // tags:
  479. // private
  480. var val = this._input.get('value');
  481. if(this.useOnBlur && val != ""){
  482. this.add(val);
  483. }
  484. },
  485. _getMatchedValueAttr: function(){
  486. // summary:
  487. // get value that match regexp in then list and return an array
  488. // tags:
  489. // private
  490. return this._getValues(dojo.hitch(this,this._matchValidator));
  491. },
  492. _getMismatchedValueAttr: function(){
  493. // summary:
  494. // get value that mismatch regexp in then list and return an array
  495. // tags:
  496. // private
  497. return this._getValues(dojo.hitch(this,this._mismatchValidator));
  498. },
  499. _getValues: function(/*function*/validator){
  500. // summary:
  501. // return values with comparator constraint
  502. // tags:
  503. // private
  504. var value = [];
  505. validator = validator||this._nullValidator;
  506. for(var i in this._items){
  507. var item = this._items[i];
  508. if(item === null){
  509. continue;
  510. }
  511. var itemValue = item.get("value");
  512. if(validator(itemValue)){
  513. value.push(itemValue);
  514. }
  515. }
  516. return value;
  517. },
  518. _nullValidator: function(/*String*/itemValue){
  519. // summary:
  520. // return true or false
  521. // tags:
  522. // private
  523. return true;
  524. },
  525. _matchValidator: function(/*String*/itemValue){
  526. // summary:
  527. // return true or false
  528. // tags:
  529. // private
  530. var re = new RegExp(this.regExpGen(this.constraints));
  531. return itemValue.match(re);
  532. },
  533. _mismatchValidator: function(/*String*/itemValue){
  534. // summary:
  535. // return true or false
  536. // tags:
  537. // private
  538. var re = new RegExp(this.regExpGen(this.constraints));
  539. return !(itemValue.match(re));
  540. },
  541. _getLastItemAttr: function(){
  542. // summary:
  543. // return the last item in list
  544. // tags:
  545. // private
  546. return this._getSomeItem();
  547. },
  548. _getSomeItem: function(/*dijit._Widget*/ item,/*String*/ position){
  549. // summary:
  550. // return the item before the one in params
  551. // tags:
  552. // private
  553. item=item||false;
  554. position=position||"last";
  555. var lastItem = null;
  556. var stop=-1;
  557. for(var i in this._items){
  558. if(this._items[i] === null){ continue; }
  559. if(position=="before" && this._items[i] === item){
  560. break;
  561. }
  562. lastItem = this._items[i];
  563. if(position=="first" ||stop==0){
  564. stop=1;
  565. break;
  566. }
  567. if(position=="after" && this._items[i] === item){
  568. stop=0;
  569. }
  570. }
  571. if(position=="after" && stop==0){
  572. lastItem = null;
  573. }
  574. return lastItem;
  575. },
  576. _getPreviousItem: function(/*dijit._Widget*/ item){
  577. // summary:
  578. // return the item before the one in params
  579. // tags:
  580. // private
  581. return this._getSomeItem(item,"before");
  582. },
  583. _getNextItem: function(/*dijit._Widget*/ item){
  584. // summary:
  585. // return the item before the one in params
  586. // tags:
  587. // private
  588. return this._getSomeItem(item,"after");
  589. },
  590. _destroyItem: function(/*dijit._Widget*/ item, /*Boolean?*/ updateValue){
  591. // summary:
  592. // destroy an item
  593. // tags:
  594. // private
  595. this._items[item.index] = null;
  596. item.destroy();
  597. this._count--;
  598. if(updateValue!==false){
  599. this._updateValues();
  600. this._setReadOnlyWhenMaxItemsReached();
  601. }
  602. },
  603. _updateValues: function(){
  604. // summary:
  605. // update this.value and the select node
  606. // tags:
  607. // private
  608. this.value = this._getValues();
  609. this._setSelectNode();
  610. },
  611. _destroyAllItems: function(){
  612. // summary:
  613. // destroy all items
  614. // tags:
  615. // private
  616. for(var i in this._items){
  617. if(this._items[i]==null){ continue; }
  618. this._destroyItem(this._items[i],false);
  619. }
  620. this._items = [];
  621. this._count = 0;
  622. this.value = null;
  623. this._setSelectNode();
  624. this._setReadOnlyWhenMaxItemsReached();
  625. },
  626. destroy: function(){
  627. // summary:
  628. // Destroy all widget
  629. this._destroyAllItems();
  630. this._lastAddedItem = null;
  631. if(!this._input){
  632. this._input.destroy();
  633. }
  634. this.inherited(arguments);
  635. }
  636. });
  637. dojo.declare("dojox.form._ListInputInputItem",
  638. [dijit._Widget, dijit._Templated],
  639. {
  640. // summary:
  641. // Item created by ListInputInput when delimiter is found
  642. // description:
  643. // Simple <li> with close button added to ListInputInput when delimiter is found
  644. templateString: "<li class=\"dijit dijitReset dijitLeft dojoxListInputItem\" dojoAttachEvent=\"onclick: onClick\" ><span dojoAttachPoint=\"labelNode\"></span></li>",
  645. // closeButtonNode: domNode
  646. // ref to the close button node
  647. closeButtonNode: null,
  648. // readOnlyItem: Boolean
  649. // if true, item is editable
  650. readOnlyItem: true,
  651. baseClass:"dojoxListInputItem",
  652. // value: String
  653. // value of item
  654. value: "",
  655. // regExp: [extension protected] String
  656. // regular expression string used to validate the input
  657. // Do not specify both regExp and regExpGen
  658. regExp: ".*",
  659. // _editBox: Widget
  660. // inline edit box
  661. _editBox: null,
  662. // _handleKeyDown: handle
  663. // handle for the keyDown connect
  664. _handleKeyDown: null,
  665. attributeMap: {
  666. value: { node: "labelNode", type: "innerHTML" }
  667. },
  668. postMixInProperties: function(){
  669. var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
  670. dojo.mixin(this, _nlsResources);
  671. this.inherited(arguments);
  672. },
  673. postCreate: function(){
  674. // summary:
  675. // Create the close button if needed
  676. this.inherited(arguments);
  677. this.closeButtonNode = dojo.create("span",{
  678. "class" : "dijitButtonNode dijitDialogCloseIcon",
  679. title : this.itemClose,
  680. onclick: dojo.hitch(this, "onClose"),
  681. onmouseenter: dojo.hitch(this, "_onCloseEnter"),
  682. onmouseleave: dojo.hitch(this, "_onCloseLeave")
  683. }, this.domNode);
  684. dojo.create("span",{
  685. "class" : "closeText",
  686. title : this.itemClose,
  687. innerHTML : "x"
  688. }, this.closeButtonNode);
  689. },
  690. startup: function(){
  691. // summary:
  692. // add the edit box
  693. this.inherited(arguments);
  694. this._createInlineEditBox();
  695. },
  696. _setReadOnlyItemAttr: function(/*Boolean*/value){
  697. // summary:
  698. // change the readonly state
  699. // tags:
  700. // private
  701. this.readOnlyItem = value;
  702. if(!value){
  703. this._createInlineEditBox();
  704. }else if(this._editBox){
  705. this._editBox.set("disabled", true);
  706. }
  707. },
  708. _createInlineEditBox: function(){
  709. // summary:
  710. // create the inline editbox if needed
  711. // tags:
  712. // private
  713. if(this.readOnlyItem){ return; }
  714. if(!this._started){ return; }
  715. if(this._editBox){
  716. this._editBox.set("disabled",false);
  717. return;
  718. }
  719. this._editBox = new dijit.InlineEditBox({
  720. value:this.value,
  721. editor: "dijit.form.ValidationTextBox",
  722. editorParams:{
  723. regExp:this.regExp
  724. }
  725. },this.labelNode);
  726. this.connect(this._editBox,"edit","_onEdit");
  727. this.connect(this._editBox,"onChange","_onCloseEdit");
  728. this.connect(this._editBox,"onCancel","_onCloseEdit");
  729. },
  730. edit: function(){
  731. // summary:
  732. // enter inline editbox in edit mode
  733. if(!this.readOnlyItem){
  734. this._editBox.edit();
  735. }
  736. },
  737. _onCloseEdit: function(/*String*/value){
  738. // summary:
  739. // call when inline editor close himself
  740. // tags:
  741. // private
  742. dojo.removeClass(this.closeButtonNode,this.baseClass + "Edited");
  743. dojo.disconnect(this._handleKeyDown);
  744. this.onChange(value);
  745. },
  746. _onEdit: function(){
  747. // summary:
  748. // call when inline editor start editing
  749. // tags:
  750. // private
  751. dojo.addClass(this.closeButtonNode,this.baseClass + "Edited");
  752. this._handleKeyDown = dojo.connect(this._editBox.editWidget,"_onKeyPress",this,"onKeyDown");
  753. this.onEdit();
  754. },
  755. _setDisabledAttr: function(/*Boolean*/value){
  756. // summary:
  757. // disable inline edit box
  758. // tags:
  759. // private
  760. if(!this.readOnlyItem){
  761. this._editBox.set("disabled", value);
  762. }
  763. },
  764. _getValueAttr: function(){
  765. // summary:
  766. // return value
  767. // tags:
  768. // private
  769. return (!this.readOnlyItem && this._started ? this._editBox.get("value") : this.value);
  770. },
  771. destroy: function(){
  772. // summary:
  773. // Destroy the inline editbox
  774. if(this._editBox){
  775. this._editBox.destroy();
  776. }
  777. this.inherited(arguments);
  778. },
  779. _onCloseEnter: function(){
  780. // summary:
  781. // Called when user hovers over close icon
  782. // tags:
  783. // private
  784. dojo.addClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
  785. },
  786. _onCloseLeave: function(){
  787. // summary:
  788. // Called when user stops hovering over close icon
  789. // tags:
  790. // private
  791. dojo.removeClass(this.closeButtonNode, "dijitDialogCloseIcon-hover");
  792. },
  793. onClose: function(){
  794. // summary:
  795. // callback when close button is clicked
  796. },
  797. onEdit: function(){
  798. // summary:
  799. // callback when widget come in edition
  800. },
  801. onClick: function(){
  802. // summary:
  803. // callback when widget is click
  804. },
  805. onChange: function(/*String*/value){
  806. // summary:
  807. // callback when widget change its content
  808. },
  809. onKeyDown: function(/*String*/value){
  810. // summary:
  811. // callback when widget get a KeyDown
  812. }
  813. });
  814. dojo.declare("dojox.form._ListInputInputBox",
  815. [dijit.form.ValidationTextBox],
  816. {
  817. // summary:
  818. // auto-sized text box
  819. // description:
  820. // Auto sized textbox based on dijit.form.TextBox
  821. // minWidth: Integer
  822. // Min width of the input box
  823. minWidth:50,
  824. // intermediateChanges: Boolean
  825. // Fires onChange for each value change or only on demand
  826. // Force to true in order to get onChanged called
  827. intermediateChanges:true,
  828. // regExp: [extension protected] String
  829. // regular expression string used to validate the input
  830. // Do not specify both regExp and regExpGen
  831. regExp: ".*",
  832. // _sizer: DomNode
  833. // Used to get size of textbox content
  834. _sizer:null,
  835. onChange: function(/*string*/value){
  836. // summary:
  837. // compute content width
  838. this.inherited(arguments);
  839. if(this._sizer === null){
  840. this._sizer = dojo.create("div",{
  841. style : {
  842. position : "absolute",
  843. left : "-10000px",
  844. top : "-10000px"
  845. }
  846. },dojo.body());
  847. }
  848. this._sizer.innerHTML = value;
  849. var w = dojo.contentBox(this._sizer).w + this.minWidth;
  850. dojo.contentBox(this.domNode,{ w : w });
  851. },
  852. destroy: function(){
  853. // summary:
  854. // destroy the widget
  855. dojo.destroy(this._sizer);
  856. this.inherited(arguments);
  857. }
  858. });
  859. }