ListInput.js 25 KB

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