Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

aui-editable-debug.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. AUI.add('aui-editable', function(A) {
  2. /**
  3. * The Editable Utility
  4. *
  5. * @module aui-editable
  6. */
  7. var Lang = A.Lang,
  8. LString = Lang.String,
  9. isFunction = Lang.isFunction,
  10. getClassName = A.getClassName,
  11. DOC = A.config.doc,
  12. HOVER = 'hover',
  13. NAME = 'editable',
  14. CSS_EDITING = getClassName(NAME, 'editing'),
  15. CSS_HOVER = getClassName(NAME, HOVER),
  16. CONTENT_BOX = 'contentBox';
  17. /**
  18. * <p><img src="assets/images/aui-editable/main.png"/></p>
  19. *
  20. * A base class for Editable, providing:
  21. * <ul>
  22. * <li>Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
  23. * <li>Edit in place elements</li>
  24. * </ul>
  25. *
  26. * Quick Example:<br/>
  27. *
  28. * <pre><code>var instance = new A.Editable({
  29. * node: '#editor'
  30. * }).render();
  31. * </code></pre>
  32. *
  33. * Check the list of <a href="Editable.html#configattributes">Configuration Attributes</a> available for
  34. * Editable.
  35. *
  36. * @param config {Object} Object literal specifying widget configuration properties.
  37. *
  38. * @class Editable
  39. * @constructor
  40. * @extends Component
  41. */
  42. var Editable = A.Component.create(
  43. {
  44. /**
  45. * Static property provides a string to identify the class.
  46. *
  47. * @property Editable.NAME
  48. * @type String
  49. * @static
  50. */
  51. NAME: NAME,
  52. /**
  53. * Static property used to define the default attribute
  54. * configuration for the Editable.
  55. *
  56. * @property Editable.ATTRS
  57. * @type Object
  58. * @static
  59. */
  60. ATTRS: {
  61. /**
  62. * <a href="ButtonItem.html">ButtonItem</a> constructor Object for the
  63. * cancelButton.
  64. *
  65. * @attribute cancelButton
  66. * @default Button constructor Object.
  67. * @type String
  68. */
  69. cancelButton: {
  70. valueFn: function() {
  71. var instance = this;
  72. return {
  73. id: 'cancel',
  74. icon: 'circle-close',
  75. handler: {
  76. context: instance,
  77. fn: instance.cancel
  78. }
  79. };
  80. }
  81. },
  82. /**
  83. * Content text.
  84. *
  85. * @attribute contentText
  86. * @default ''
  87. * @type String
  88. */
  89. contentText: {
  90. value: '',
  91. setter: function(value) {
  92. var instance = this;
  93. value = Lang.trim(value);
  94. instance._toText(value);
  95. return value;
  96. }
  97. },
  98. /**
  99. * Event type to initialize the editable.
  100. *
  101. * @attribute eventType
  102. * @default 'click'
  103. * @type String
  104. */
  105. eventType: {
  106. value: 'click'
  107. },
  108. /**
  109. * Function to format the input text displayed on the input.
  110. *
  111. * @attribute formatInput
  112. * @default null
  113. * @type function
  114. */
  115. formatInput: {
  116. value: null,
  117. validator: isFunction
  118. },
  119. /**
  120. * Function to format the output text displayed on the input.
  121. *
  122. * @attribute formatOutput
  123. * @default null
  124. * @type function
  125. */
  126. formatOutput: {
  127. value: null,
  128. validator: isFunction
  129. },
  130. /**
  131. * Array with icons for the <a href="Toolbar.html">Toolbar</a>.
  132. *
  133. * @attribute icons
  134. * @default []
  135. * @type Array
  136. */
  137. icons: {
  138. value: []
  139. },
  140. /**
  141. * Type of the input used to edit the <a
  142. * href="Editable.html#config_node">node</a>.
  143. *
  144. * @attribute inputType
  145. * @default 'text'
  146. * @type String
  147. */
  148. inputType: {
  149. value: 'text',
  150. setter: function(value) {
  151. var instance = this;
  152. if (value != 'text' && value != 'textarea') {
  153. value = A.Attribute.INVALID_VALUE;
  154. }
  155. return value;
  156. }
  157. },
  158. /**
  159. * Node to setup the editable.
  160. *
  161. * @attribute node
  162. * @type Node
  163. */
  164. node: {
  165. setter: function(value) {
  166. var node = A.one(value);
  167. if (!node) {
  168. A.error('AUI.Editable: Invalid Node Given: ' + value);
  169. }
  170. return node;
  171. }
  172. },
  173. /**
  174. * Node to render the editable.
  175. *
  176. * @attribute renderTo
  177. * @type String | Node
  178. */
  179. renderTo: {
  180. value: DOC.body,
  181. setter: function(value) {
  182. var instance = this;
  183. var node;
  184. if (value == 'node') {
  185. node = instance.get(value);
  186. }
  187. else {
  188. node = A.one(value);
  189. }
  190. if (!node) {
  191. A.error('AUI.Editable: Invalid renderTo Given: ' + value);
  192. }
  193. return node;
  194. }
  195. },
  196. /**
  197. * <a href="ButtonItem.html">ButtonItem</a> constructor Object for the
  198. * saveButton.
  199. *
  200. * @attribute saveButton
  201. * @default Button constructor Object.
  202. * @type String
  203. */
  204. saveButton: {
  205. valueFn: function() {
  206. var instance = this;
  207. return {
  208. id: 'save',
  209. icon: 'circle-check',
  210. handler: {
  211. context: instance,
  212. fn: instance.save
  213. }
  214. };
  215. }
  216. },
  217. visible: {
  218. value: false
  219. }
  220. },
  221. UI_ATTRS: ['node'],
  222. prototype: {
  223. /**
  224. * Construction logic executed during Editable instantiation. Lifecycle.
  225. *
  226. * @method initializer
  227. * @protected
  228. */
  229. initializer: function() {
  230. var instance = this;
  231. instance._uiSetNode(instance.get('node'));
  232. instance._createEvents();
  233. },
  234. /**
  235. * Create the DOM structure for the Editable. Lifecycle.
  236. *
  237. * @method renderUI
  238. * @protected
  239. */
  240. renderUI: function() {
  241. var instance = this;
  242. var contentBox = instance.get(CONTENT_BOX);
  243. var inputType = instance.get('inputType');
  244. var comboConfig = {};
  245. var icons = instance.get('icons');
  246. if (icons !== false) {
  247. var cancelButton = instance.get('cancelButton');
  248. var saveButton = instance.get('saveButton');
  249. if (cancelButton !== false) {
  250. icons.push(cancelButton);
  251. }
  252. if (saveButton !== false) {
  253. icons.push(saveButton);
  254. }
  255. comboConfig.icons = icons;
  256. }
  257. if (inputType != 'text') {
  258. A.mix(
  259. comboConfig,
  260. {
  261. field: {
  262. autoSize: true
  263. },
  264. fieldWidget: A.Textarea
  265. }
  266. );
  267. }
  268. var comboBox = new A.Combobox(comboConfig).render(contentBox);
  269. instance._comboBox = comboBox;
  270. instance.inputNode = comboBox.get('node');
  271. },
  272. /**
  273. * Bind the events on the Editable UI. Lifecycle.
  274. *
  275. * @method bindUI
  276. * @protected
  277. */
  278. bindUI: function() {
  279. var instance = this;
  280. var contentBox = instance.get(CONTENT_BOX);
  281. var node = instance.get('node');
  282. var inputNode = instance.inputNode;
  283. inputNode.on('keypress', instance._onKeypressEditable, instance);
  284. instance.after('contentTextChange', instance._syncContentText);
  285. contentBox.swallowEvent('click');
  286. A.getDoc().after('click', instance._afterFocusedChangeEditable, instance);
  287. },
  288. /**
  289. * Sync the Editable UI. Lifecycle.
  290. *
  291. * @method syncUI
  292. * @protected
  293. */
  294. syncUI: function() {
  295. var instance = this;
  296. var currentText = instance.get('node').get('innerHTML');
  297. currentText = currentText.replace(/\n|\r/gim, '');
  298. currentText = Lang.trim(currentText);
  299. currentText = instance._toText(currentText);
  300. instance._setInput(currentText);
  301. instance.set(
  302. 'contentText',
  303. currentText,
  304. {
  305. initial: true
  306. }
  307. );
  308. },
  309. /**
  310. * Cancel the editable. Return to the original state.
  311. *
  312. * @method cancel
  313. */
  314. cancel: function() {
  315. var instance = this;
  316. instance.fire('cancel');
  317. },
  318. /**
  319. * Save the editable. Fires the
  320. * <a href="Editable.html#event_save">save</a> event.
  321. *
  322. * @method save
  323. */
  324. save: function(event) {
  325. var instance = this;
  326. instance.fire('save');
  327. },
  328. /**
  329. * Fires the <a href="Editable.html#event_stopEditing">stopEditing</a>
  330. * event.
  331. *
  332. * @method _afterFocusedChangeEditable
  333. * @param {EventFacade} event
  334. * @protected
  335. */
  336. _afterFocusedChangeEditable: function(event) {
  337. var instance = this;
  338. instance.fire('stopEditing', instance.get('visible'));
  339. },
  340. /**
  341. * Create the Events.
  342. *
  343. * @method _createEvents
  344. * @protected
  345. */
  346. _createEvents: function() {
  347. var instance = this;
  348. /**
  349. * Handles the startEditing event.
  350. *
  351. * @event startEditing
  352. * @preventable _defStartEditingFn
  353. * @param {Event.Facade} event The startEditing event.
  354. * @type {Event.Custom}
  355. */
  356. instance.publish(
  357. 'startEditing',
  358. {
  359. bubbles: true,
  360. defaultFn: instance._defStartEditingFn,
  361. emitFacade: true,
  362. queable: false
  363. }
  364. );
  365. /**
  366. * Handles the stopEditing event.
  367. *
  368. * @event stopEditing
  369. * @preventable _defStopEditingFn
  370. * @param {Event.Facade} event The stopEditing event.
  371. * @type {Event.Custom}
  372. */
  373. instance.publish(
  374. 'stopEditing',
  375. {
  376. bubbles: true,
  377. defaultFn: instance._defStopEditingFn,
  378. emitFacade: true,
  379. queable: false
  380. }
  381. );
  382. /**
  383. * Handles the save event.
  384. *
  385. * @event save
  386. * @preventable _defSaveFn
  387. * @param {Event.Facade} event The save event.
  388. * @type {Event.Custom}
  389. */
  390. instance.publish(
  391. 'save',
  392. {
  393. bubbles: true,
  394. defaultFn: instance._defSaveFn,
  395. emitFacade: true,
  396. queable: false
  397. }
  398. );
  399. /**
  400. * Handles the cancel event.
  401. *
  402. * @event cancel
  403. * @preventable _defCancelFn
  404. * @param {Event.Facade} event The cancel event.
  405. * @type {Event.Custom}
  406. */
  407. instance.publish(
  408. 'cancel',
  409. {
  410. bubbles: true,
  411. defaultFn: instance._defCancelFn,
  412. emitFacade: true,
  413. queable: false
  414. }
  415. );
  416. },
  417. /**
  418. * Fires the cancel event.
  419. *
  420. * @method _defCancelFn
  421. * @param {EventFacade} event cancel event facade
  422. * @protected
  423. */
  424. _defCancelFn: function(event) {
  425. var instance = this;
  426. instance.fire('stopEditing', false);
  427. },
  428. /**
  429. * Fires the save event.
  430. *
  431. * @method _defSaveFn
  432. * @param {EventFacade} event save event facade
  433. * @protected
  434. */
  435. _defSaveFn: function(event) {
  436. var instance = this;
  437. instance.fire('stopEditing', true);
  438. },
  439. /**
  440. * Fires the startEditing event.
  441. *
  442. * @method _defStartEditingFn
  443. * @param {EventFacade} event startEditing event facade
  444. * @protected
  445. */
  446. _defStartEditingFn: function(event) {
  447. var instance = this;
  448. var boundingBox = instance.get('boundingBox');
  449. var node = instance.get('node');
  450. var inputNode = instance.inputNode;
  451. var nodeHeight = node.get('offsetHeight');
  452. var nodeWidth = node.get('offsetWidth');
  453. instance.show();
  454. node.addClass(CSS_EDITING);
  455. var xy = node.getXY();
  456. boundingBox.setStyles(
  457. {
  458. height: nodeHeight + 'px',
  459. left: xy[0] + 'px',
  460. top: xy[1] + 'px',
  461. width: nodeWidth + 'px'
  462. }
  463. );
  464. var inputField = instance._comboBox._field;
  465. inputField.set('width', nodeWidth);
  466. inputField.fire('adjustSize');
  467. inputNode.focus();
  468. inputNode.select();
  469. },
  470. /**
  471. * Fires the stopEditing event.
  472. *
  473. * @method _defStopEditingFn
  474. * @param {EventFacade} event stopEditing event facade
  475. * @protected
  476. */
  477. _defStopEditingFn: function(event, save) {
  478. var instance = this;
  479. instance.hide();
  480. instance.get('node').removeClass(CSS_EDITING);
  481. if (save) {
  482. instance.set('contentText', instance.inputNode.get('value'));
  483. }
  484. else {
  485. instance._setInput(instance.get('contentText'));
  486. }
  487. },
  488. /**
  489. * Fires <code>onkeypress</code> occurs on the editable element.
  490. *
  491. * @method _onKeypressEditable
  492. * @param {EventFacade} event
  493. * @protected
  494. */
  495. _onKeypressEditable: function(event) {
  496. var instance = this;
  497. if (event.isKey('ESC')) {
  498. event.preventDefault();
  499. instance.cancel();
  500. }
  501. else if (event.isKey('ENTER') && (instance.get('inputType') == 'text')) {
  502. instance.save();
  503. }
  504. },
  505. /**
  506. * Fires <code>onmouseenter</code> occurs on the editable element.
  507. *
  508. * @method _onMouseEnterEditable
  509. * @param {EventFacade} event
  510. * @protected
  511. */
  512. _onMouseEnterEditable: function(event) {
  513. var instance = this;
  514. instance.get('node').addClass(CSS_HOVER);
  515. },
  516. /**
  517. * Fires <code>onmouseleave</code> occurs on the editable element.
  518. *
  519. * @method _onMouseLeaveEditable
  520. * @param {EventFacade} event
  521. * @protected
  522. */
  523. _onMouseLeaveEditable: function(event) {
  524. var instance = this;
  525. instance.get('node').removeClass(CSS_HOVER);
  526. },
  527. /**
  528. * Set the value of the <a
  529. * href="Editable.html#property_inputNode">inputNode</a>.
  530. *
  531. * @method _setInput
  532. * @param {String} value Value of the input.
  533. * @protected
  534. */
  535. _setInput: function(value) {
  536. var instance = this;
  537. var inputFormatter = instance.get('formatInput');
  538. if (inputFormatter) {
  539. value = inputFormatter.call(instance, value);
  540. }
  541. else {
  542. value = instance._toText(value);
  543. }
  544. instance.inputNode.set('value', LString.unescapeEntities(value));
  545. },
  546. /**
  547. * Set the <code>innerHTML</code> of the <a
  548. * href="Editable.html#config_node">node</a>.
  549. *
  550. * @method _setOutput
  551. * @param {String} value
  552. * @protected
  553. */
  554. _setOutput: function(value) {
  555. var instance = this;
  556. var outputFormatter = instance.get('formatOutput');
  557. if (outputFormatter) {
  558. value = outputFormatter.call(instance, value);
  559. }
  560. else {
  561. value = instance._toHTML(value);
  562. }
  563. instance.get('node').set('innerHTML', A.Escape.html(value));
  564. },
  565. /**
  566. * Fires when start editing.
  567. *
  568. * @method _startEditing
  569. * @param {EventFacade} event
  570. * @protected
  571. */
  572. _startEditing: function(event) {
  573. var instance = this;
  574. if (!instance.get('rendered')) {
  575. instance.render(instance.get('renderTo'));
  576. }
  577. instance.fire('startEditing');
  578. event.halt();
  579. },
  580. /**
  581. * Sync the content text.
  582. *
  583. * @method _syncContentText
  584. * @param {EventFacade} event
  585. * @protected
  586. */
  587. _syncContentText: function(event) {
  588. var instance = this;
  589. if (!event.initial) {
  590. var contentText = event.newVal;
  591. instance._setInput(contentText);
  592. instance._setOutput(contentText);
  593. }
  594. },
  595. /**
  596. * Converts the new lines <code>\n</code> to <code><br/></code> (i.e.,
  597. * nl2br).
  598. *
  599. * @method _toHTML
  600. * @param {String} text Input text.
  601. * @protected
  602. * @return {String}
  603. */
  604. _toHTML: function(text) {
  605. var instance = this;
  606. return String(text).replace(/\n/gim, '<br/>');
  607. },
  608. /**
  609. * Converts HTML to text.
  610. *
  611. * @method _toText
  612. * @param {String} text HTML input.
  613. * @protected
  614. * @return {String}
  615. */
  616. _toText: function(text) {
  617. var instance = this;
  618. text = String(text);
  619. text = text.replace(/<br\s*\/?>/gim, '\n');
  620. text = text.replace(/(<\/?[^>]+>|\t)/gim, '');
  621. return text;
  622. },
  623. /**
  624. * Handles the updating of the UI when the node is set.
  625. *
  626. * @method _uiSetNode
  627. * @param {Node} node.
  628. * @protected
  629. */
  630. _uiSetNode: function(node) {
  631. var instance = this;
  632. if (instance._mouseEnterHandler) {
  633. instance._mouseEnterHandler.detach();
  634. }
  635. if (instance._mouseLeaveHandler) {
  636. instance._mouseLeaveHandler.detach();
  637. }
  638. if (instance._interactionHandler) {
  639. instance._interactionHandler.detach();
  640. }
  641. var eventType = instance.get('eventType');
  642. instance._mouseEnterHandler = node.on('mouseenter', instance._onMouseEnterEditable, instance);
  643. instance._mouseLeaveHandler = node.on('mouseleave', instance._onMouseLeaveEditable, instance);
  644. instance._interactionHandler = node.on(eventType, instance._startEditing, instance);
  645. }
  646. }
  647. }
  648. );
  649. A.Editable = Editable;
  650. }, '@VERSION@' ,{requires:['aui-base','aui-form-combobox','escape'], skinnable:true});