transfer.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. // Generated by CoffeeScript 2.0.0-beta4
  2. (function() {
  3. // ------------------------------------------------------------------------
  4. // 變數與常數設置
  5. // ------------------------------------------------------------------------
  6. // 模組名稱。
  7. var Attribute, ClassName, EVENT_NAMESPACE, Error, Event, MODULE_NAMESPACE, NAME, Selector, Settings, TARGET;
  8. NAME = 'transfer';
  9. // 模組事件鍵名。
  10. EVENT_NAMESPACE = `.${NAME}`;
  11. // 模組命名空間。
  12. MODULE_NAMESPACE = `module-${NAME}`;
  13. // 模組設定。
  14. Settings = {
  15. // 消音所有提示,甚至是錯誤訊息。
  16. silent: false,
  17. // 顯示除錯訊息。
  18. debug: true,
  19. // 監聽 DOM 結構異動並自動重整快取。
  20. observeChanges: true,
  21. // 當值移動時所會呼叫的回呼函式。
  22. onMove: (values, to) => {},
  23. // 當值有所移動、新增或移除時所會呼叫的函式。
  24. onChange: (value) => {},
  25. // 當值被選取時所會呼叫的函式。
  26. onSelect: (value) => {},
  27. // 當值被取消選取時所會呼叫的函式。
  28. onUnselect: (value) => {},
  29. // 搜尋時所會呼叫的函式。
  30. onSearch: () => {}
  31. };
  32. // 事件名稱。
  33. Event = {
  34. CHANGE: `change${EVENT_NAMESPACE}`,
  35. SELECT: `select${EVENT_NAMESPACE}`,
  36. UNSELECT: `unselect${EVENT_NAMESPACE}`,
  37. MOVE: `move${EVENT_NAMESPACE}`,
  38. SEARCH: `search${EVENT_NAMESPACE}`,
  39. CLICK: `click${EVENT_NAMESPACE}`
  40. };
  41. // 元素屬性標籤。
  42. Attribute = {
  43. VALUE: 'data-value'
  44. };
  45. // 目標
  46. TARGET = {
  47. DESTINATION: 'destination',
  48. SOURCE: 'source'
  49. };
  50. // 樣式名稱。
  51. ClassName = {
  52. ITEM: 'item',
  53. ADD: 'add',
  54. SELECTED: 'selected'
  55. };
  56. // 選擇器名稱。
  57. Selector = {
  58. DESTINATION_ITEMS: '.destination.list .items',
  59. DESTINATION_ITEM: '.destination.list .items .item',
  60. DESTINATION_SELECTED_ITEM: '.destination.list .items .selected.item',
  61. SOURCE_ITEMS: '.source.list .items',
  62. SOURCE_ITEM: '.source.list .items .item',
  63. SOURCE_SELECTED_ITEM: '.source.list .items .selected.item',
  64. ANY_ITEM: '.items .item',
  65. ACTION_BUTTON: '.ts.add.button, .ts.remove.button',
  66. ITEM: (value) => {
  67. return `.items .item[data-value='${value}']`;
  68. },
  69. SELECTED_ITEM: (value) => {
  70. return `.items .selected.item[data-value='${value}']`;
  71. }
  72. };
  73. // 錯誤訊息。
  74. Error = {};
  75. // ------------------------------------------------------------------------
  76. // 模組註冊
  77. // ------------------------------------------------------------------------
  78. ts.register({NAME, MODULE_NAMESPACE, Error, Settings}, ({$allModules, $this, element, debug, settings}) => {
  79. var module;
  80. // ------------------------------------------------------------------------
  81. // 區域變數
  82. // ------------------------------------------------------------------------
  83. // ------------------------------------------------------------------------
  84. // 模組定義
  85. // ------------------------------------------------------------------------
  86. return module = {
  87. get: {
  88. values: (from) => {
  89. var $item, values;
  90. values = [];
  91. switch (from) {
  92. case TARGET.DESTINATION:
  93. $item = $this.find(Selector.DESTINATION_ITEM);
  94. break;
  95. case TARGET.SOURCE:
  96. $item = $this.find(Selector.SOURCE_ITEM);
  97. }
  98. $item.each(function() {
  99. return values.push(ts(this).attr(Attribute.VALUE));
  100. });
  101. return values;
  102. },
  103. selected: (from) => {
  104. var $item, selected;
  105. selected = [];
  106. switch (from) {
  107. case TARGET.DESTINATION:
  108. $item = $this.find(Selector.DESTINATION_SELECTED_ITEM);
  109. break;
  110. case TARGET.SOURCE:
  111. $item = $this.find(Selector.SOURCE_SELECTED_ITEM);
  112. }
  113. $item.each(function() {
  114. return selected.push(ts(this).attr(Attribute.VALUE));
  115. });
  116. return selected;
  117. },
  118. destination: {
  119. values: () => {
  120. return module.get.values(TARGET.DESTINATION);
  121. },
  122. selected: () => {
  123. return module.get.selected(TARGET.DESTINATION);
  124. }
  125. },
  126. source: {
  127. values: () => {
  128. return module.get.values(TARGET.SOURCE);
  129. },
  130. selected: () => {
  131. return module.get.selected(TARGET.SOURCE);
  132. }
  133. },
  134. $item: (value) => {
  135. return $this.find(Selector.ITEM(value));
  136. }
  137. },
  138. create: {
  139. $value: (value, text) => {
  140. return ts('<div>').html(text).addClass(ClassName.ITEM).attr(Attribute.VALUE, value);
  141. }
  142. },
  143. add: {
  144. destination: {
  145. value: (value, text) => {
  146. module.add.value(TARGET.DESTINATION, value, text);
  147. return $allModules;
  148. }
  149. },
  150. source: {
  151. value: (value, text) => {
  152. module.add.value(TARGET.SOURCE, value, text);
  153. return $allModules;
  154. }
  155. },
  156. value: (to, value, text) => {
  157. var $targetItems, i, item, len;
  158. switch (to) {
  159. case TARGET.DESTINATION:
  160. $targetItems = $this.find(Selector.DESTINATION_ITEMS);
  161. break;
  162. case TARGET.SOURCE:
  163. $targetItems = $this.find(Selector.SOURCE_ITEMS);
  164. }
  165. if (Array.isArray(value)) {
  166. for (i = 0, len = value.length; i < len; i++) {
  167. item = value[i];
  168. if (module.has.value(item.value)) {
  169. continue;
  170. }
  171. $targetItems.append(module.create.$value(item.value, item.text));
  172. }
  173. } else {
  174. if (module.has.value(value)) {
  175. return;
  176. }
  177. $targetItems.append(module.create.$value(value, text));
  178. }
  179. module.scroll.toBottom($targetItems);
  180. return module.trigger.change(value);
  181. }
  182. },
  183. has: {
  184. value: (value) => {
  185. return module.get.$item(value).length !== 0;
  186. }
  187. },
  188. remove: {
  189. value: (value) => {
  190. var i, item, len;
  191. if (Array.isArray(value)) {
  192. for (i = 0, len = value.length; i < len; i++) {
  193. item = value[i];
  194. module.get.$item(item).remove();
  195. }
  196. } else {
  197. module.get.$item(value).remove();
  198. }
  199. module.trigger.change(value);
  200. return $allModules;
  201. }
  202. },
  203. set: {
  204. destination: {
  205. value: (values) => {
  206. $this.find(Selector.DESTINATION_ITEM).remove();
  207. module.add.destination.value(value);
  208. return $allModules;
  209. }
  210. },
  211. source: {
  212. value: (values) => {
  213. $this.find(Selector.SOURCE_ITEM).remove();
  214. module.add.source.value(value);
  215. return $allModules;
  216. }
  217. }
  218. },
  219. scroll: {
  220. toBottom: ($items) => {
  221. var items;
  222. items = $items.get();
  223. return items.scrollTop = items.scrollHeight;
  224. },
  225. destination: {
  226. toBottom: () => {
  227. return module.scroll.toBottom($this.find(Selector.DESTINATION_ITEMS));
  228. }
  229. },
  230. source: {
  231. toBottom: () => {
  232. return module.scroll.toBottom($this.find(Selector.SOURCE_ITEMS));
  233. }
  234. }
  235. },
  236. move: {
  237. values: (to, values) => {
  238. var $targetItems, i, len, value;
  239. if (!Array.isArray(values)) {
  240. values = [values];
  241. }
  242. if (values.length === 0) {
  243. return;
  244. }
  245. switch (to) {
  246. case TARGET.DESTINATION:
  247. $targetItems = $this.find(Selector.DESTINATION_ITEMS);
  248. break;
  249. case TARGET.SOURCE:
  250. $targetItems = $this.find(Selector.SOURCE_ITEMS);
  251. }
  252. for (i = 0, len = values.length; i < len; i++) {
  253. value = values[i];
  254. ts(Selector.ITEM(value)).appendTo($targetItems);
  255. module.unselect.value(value);
  256. }
  257. module.scroll.toBottom($targetItems);
  258. module.trigger.move(values, to);
  259. return module.trigger.change(values);
  260. },
  261. to: {
  262. destination: (value) => {
  263. module.move.values(TARGET.DESTINATION, value);
  264. return $allModules;
  265. },
  266. source: (value) => {
  267. module.move.values(TARGET.SOURCE, value);
  268. return $allModules;
  269. }
  270. }
  271. },
  272. select: {
  273. value: (value) => {
  274. module.get.$item(value).addClass(ClassName.SELECTED);
  275. module.trigger.select(value);
  276. return $allModules;
  277. },
  278. destination: {
  279. all: () => {
  280. $this.find(Selector.DESTINATION_ITEM).each(function() {
  281. return module.select.value(ts(this).attr(Attribute.VALUE));
  282. });
  283. return $allModules;
  284. }
  285. },
  286. source: {
  287. all: () => {
  288. $this.find(Selector.SOURCE_ITEM).each(function() {
  289. return module.select.value(ts(this).attr(Attribute.VALUE));
  290. });
  291. return $allModules;
  292. }
  293. }
  294. },
  295. unselect: {
  296. value: (value) => {
  297. module.get.$item(value).removeClass(ClassName.SELECTED);
  298. module.trigger.unselect(value);
  299. return $allModules;
  300. },
  301. all: () => {
  302. module.unselect.destination.all();
  303. module.unselect.source.all();
  304. return $allModules;
  305. },
  306. destination: {
  307. all: () => {
  308. $this.find(Selector.DESTINATION_ITEM).each(function() {
  309. return module.unselect.value(ts(this).attr(Attribute.VALUE));
  310. });
  311. return $allModules;
  312. }
  313. },
  314. source: {
  315. all: () => {
  316. $this.find(Selector.SOURCE_ITEM).each(function() {
  317. return module.unselect.value(ts(this).attr(Attribute.VALUE));
  318. });
  319. return $allModules;
  320. }
  321. }
  322. },
  323. is: {
  324. selected: (value) => {
  325. return $this.find(Selector.SELECTED_ITEM(value)).length !== 0;
  326. }
  327. },
  328. trigger: {
  329. change: (value) => {
  330. return $this.trigger(Event.CHANGE, element, value);
  331. },
  332. select: (value) => {
  333. return $this.trigger(Event.SELECT, element, value);
  334. },
  335. unselect: (value) => {
  336. return $this.trigger(Event.UNSELECT, element, value);
  337. },
  338. move: (values, to) => {
  339. return $this.trigger(Event.MOVE, element, values, to);
  340. }
  341. },
  342. bind: {
  343. events: () => {
  344. $this.on(Event.CHANGE, (event, context, value) => {
  345. debug("發生 CHANGE 事件", context, value);
  346. return settings.onChange.call(context, event, value);
  347. });
  348. $this.on(Event.SELECT, (event, context, value) => {
  349. debug("發生 SELECT 事件", context, value);
  350. return settings.onSelect.call(context, event, value);
  351. });
  352. $this.on(Event.UNSELECT, (event, context, value) => {
  353. debug("發生 UNSELECT 事件", context, value);
  354. return settings.onUnselect.call(context, event, value);
  355. });
  356. $this.on(Event.MOVE, (event, context, values, to) => {
  357. debug("發生 MOVE 事件", context, values, to);
  358. return settings.onMove.call(context, event, values, to);
  359. });
  360. $this.on(Event.CLICK, Selector.ANY_ITEM, function() {
  361. var value;
  362. debug("發生 CLICK 事件", this);
  363. value = ts(this).attr(Attribute.VALUE);
  364. if (module.is.selected(value)) {
  365. return module.unselect.value(value);
  366. } else {
  367. return module.select.value(value);
  368. }
  369. });
  370. return $this.on(Event.CLICK, Selector.ACTION_BUTTON, function() {
  371. debug("發生 BUTTON CLICK 事件", this);
  372. if (ts(this).hasClass(ClassName.ADD)) {
  373. return module.move.to.destination(module.get.source.selected());
  374. } else {
  375. return module.move.to.source(module.get.destination.selected());
  376. }
  377. });
  378. }
  379. },
  380. // ------------------------------------------------------------------------
  381. // 基礎方法
  382. // ------------------------------------------------------------------------
  383. initialize: () => {
  384. debug('初始化穿梭框', element);
  385. return module.bind.events();
  386. },
  387. instantiate: () => {
  388. return debug('實例化穿梭框', element);
  389. },
  390. refresh: () => {
  391. return $allModules;
  392. },
  393. destroy: () => {
  394. debug('摧毀穿梭框', element);
  395. $this.removeData(MODULE_NAMESPACE).off(EVENT_NAMESPACE);
  396. return $allModules;
  397. }
  398. };
  399. });
  400. }).call(this);