cron-ui.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. /* eslint-disable */
  2. import $ from 'jquery';
  3. /*
  4. * This file is part of the Arnapou jqCron package.
  5. *
  6. * (c) Arnaud Buathier <arnaud@arnapou.net>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. /**
  12. * Default settings
  13. */
  14. var jqCronDefaultSettings = {
  15. texts: {},
  16. monthdays: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
  17. hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
  18. hour_labels: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
  19. minutes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
  20. lang: 'en',
  21. enabled_minute: false,
  22. enabled_hour: true,
  23. enabled_day: true,
  24. enabled_week: true,
  25. enabled_month: true,
  26. enabled_year: true,
  27. multiple_dom: false,
  28. multiple_month: false,
  29. multiple_mins: false,
  30. multiple_dow: false,
  31. multiple_time_hours: false,
  32. multiple_time_minutes: false,
  33. numeric_zero_pad: false,
  34. default_period: 'day',
  35. default_value: '',
  36. no_reset_button: true,
  37. disabled: false,
  38. bind_to: null,
  39. bind_method: {
  40. set: function($element, value) {
  41. $element.is(':input') ? $element.val(value) : $element.data('jqCronValue', value);
  42. },
  43. get: function($element) {
  44. return $element.is(':input') ? $element.val() : $element.data('jqCronValue');
  45. }
  46. }
  47. };
  48. /**
  49. * Custom extend of json for jqCron settings.
  50. * We don't use jQuery.extend because simple extend does not fit our needs, and deep extend has a bad
  51. * feature for us : it replaces keys of "Arrays" instead of replacing the full array.
  52. */
  53. (function($){
  54. var extend = function(dst, src) {
  55. for(var i in src) {
  56. if($.isPlainObject(src[i])) {
  57. dst[i] = extend(dst[i] && $.isPlainObject(dst[i]) ? dst[i] : {}, src[i]);
  58. }
  59. else if($.isArray(src[i])) {
  60. dst[i] = src[i].slice(0);
  61. }
  62. else if(src[i] !== undefined) {
  63. dst[i] = src[i];
  64. }
  65. }
  66. return dst;
  67. };
  68. this.jqCronMergeSettings = function(obj) {
  69. return extend(extend({}, jqCronDefaultSettings), obj || {});
  70. };
  71. }).call(window, $);
  72. /**
  73. * Shortcut to get the instance of jqCron instance from one jquery object
  74. */
  75. (function($){
  76. $.fn.jqCronGetInstance = function() {
  77. return this.data('jqCron');
  78. };
  79. }).call(window, $);
  80. /**
  81. * Main plugin
  82. */
  83. (function($){
  84. $.fn.jqCron = function(settings) {
  85. var saved_settings = settings;
  86. return this.each(function() {
  87. var cron, saved;
  88. var $this = $(this);
  89. var settings = jqCronMergeSettings(saved_settings); // clone settings
  90. var translations = settings.texts[settings.lang];
  91. if (typeof(translations) !== 'object' || $.isEmptyObject(translations)) {
  92. console && console.error(
  93. 'Missing translations for language "' + settings.lang + '". ' +
  94. 'Please include jqCron.' + settings.lang + '.js or manually provide ' +
  95. 'the necessary translations when calling $.fn.jqCron().'
  96. );
  97. return;
  98. }
  99. if(!settings.jquery_container) {
  100. if($this.is(':container')) {
  101. settings.jquery_element = $this.uniqueId('jqCron');
  102. }
  103. else if($this.is(':autoclose')) {
  104. // delete already generated dom if exists
  105. if($this.next('.jqCron').length == 1) {
  106. $this.next('.jqCron').remove();
  107. }
  108. // generate new
  109. settings.jquery_element = $('<span class="jqCron"></span>').uniqueId('jqCron').insertAfter($this);
  110. }
  111. else {
  112. console && console.error(settings.texts[settings.lang].error1.replace('%s', this.tagName));
  113. return;
  114. }
  115. }
  116. // autoset bind_to if it is an input
  117. if($this.is(':input')) {
  118. settings.bind_to = settings.bind_to || $this;
  119. }
  120. // init cron object
  121. if(settings.bind_to){
  122. if(settings.bind_to.is(':input')) {
  123. // auto bind from input to object if an input, textarea ...
  124. settings.bind_to.blur(function(){
  125. var value = settings.bind_method.get(settings.bind_to);
  126. $this.jqCronGetInstance().setCron(value);
  127. });
  128. }
  129. saved = settings.bind_method.get(settings.bind_to);
  130. cron = new jqCron(settings);
  131. cron.setCron(saved);
  132. }
  133. else {
  134. cron = new jqCron(settings);
  135. }
  136. $(this).data('jqCron', cron);
  137. });
  138. };
  139. }).call(window, $);
  140. /**
  141. * jqCron class
  142. */
  143. (function($){
  144. var jqCronInstances = [];
  145. function jqCron(settings) {
  146. var _initialized = false;
  147. var _self = this;
  148. var _$elt = this;
  149. var _$obj = $('<span class="jqCron-container"></span>');
  150. var _$blocks = $('<span class="jqCron-blocks"></span>');
  151. var _$blockPERIOD = $('<span class="jqCron-period"></span>');
  152. var _$blockDOM = $('<span class="jqCron-dom"></span>');
  153. var _$blockMONTH = $('<span class="jqCron-month"></span>');
  154. var _$blockMINS = $('<span class="jqCron-mins"></span>');
  155. var _$blockDOW = $('<span class="jqCron-dow"></span>');
  156. var _$blockTIME = $('<span class="jqCron-time"></span>');
  157. var _$cross = $('<span class="jqCron-cross">&#10008;</span>');
  158. var _selectors = [];
  159. var _selectorPeriod, _selectorMins, _selectorTimeH, _selectorTimeM, _selectorDow, _selectorDom, _selectorMonth;
  160. // instanciate a new selector
  161. function newSelector($block, multiple, type){
  162. var selector = new jqCronSelector(_self, $block, multiple, type);
  163. selector.$.bind('selector:open', function(){
  164. // we close all opened selectors of all other jqCron
  165. for(var n = jqCronInstances.length; n--; ){
  166. if(jqCronInstances[n] != _self) {
  167. jqCronInstances[n].closeSelectors();
  168. }
  169. else {
  170. // we close all other opened selectors of this jqCron
  171. for(var o = _selectors.length; o--; ){
  172. if(_selectors[o] != selector) {
  173. _selectors[o].close();
  174. }
  175. }
  176. }
  177. }
  178. });
  179. selector.$.bind('selector:change', function(){
  180. var boundChanged = false;
  181. // don't propagate if not initialized
  182. if(!_initialized) return;
  183. // bind data between two minute selectors (only if they have the same multiple settings)
  184. if(settings.multiple_mins == settings.multiple_time_minutes) {
  185. if(selector == _selectorMins) {
  186. boundChanged = _selectorTimeM.setValue(_selectorMins.getValue());
  187. }
  188. else if(selector == _selectorTimeM) {
  189. boundChanged = _selectorMins.setValue(_selectorTimeM.getValue());
  190. }
  191. }
  192. // we propagate the change event to the main object
  193. boundChanged || _$obj.trigger('cron:change', _self.getCron());
  194. });
  195. _selectors.push(selector);
  196. return selector;
  197. }
  198. // disable the selector
  199. this.disable = function(){
  200. _$obj.addClass('disable');
  201. settings.disable = true;
  202. _self.closeSelectors();
  203. };
  204. // return if the selector is disabled
  205. this.isDisabled = function() {
  206. return settings.disable == true;
  207. };
  208. // enable the selector
  209. this.enable = function(){
  210. _$obj.removeClass('disable');
  211. settings.disable = false;
  212. };
  213. // get cron value
  214. this.getCron = function(){
  215. var period = _selectorPeriod.getValue();
  216. var items = ['*', '*', '*', '*', '*'];
  217. if(period == 'hour') {
  218. items[0] = _selectorMins.getCronValue();
  219. }
  220. if(period == 'day' || period == 'week' || period == 'month' || period == 'year') {
  221. items[0] = _selectorTimeM.getCronValue();
  222. items[1] = _selectorTimeH.getCronValue();
  223. }
  224. if(period == 'month' || period == 'year') {
  225. items[2] = _selectorDom.getCronValue();
  226. }
  227. if(period == 'year') {
  228. items[3] = _selectorMonth.getCronValue();
  229. }
  230. if(period == 'week') {
  231. items[4] = _selectorDow.getCronValue();
  232. }
  233. return items.join(' ');
  234. };
  235. // set cron (string like * * * * *)
  236. this.setCron = function(str) {
  237. if(!str) return;
  238. try {
  239. str = str.replace(/\s+/g, ' ').replace(/^ +/, '').replace(/ +$/, ''); // sanitize
  240. var mask = str.replace(/[^\* ]/g, '-').replace(/-+/g, '-').replace(/ +/g, '');
  241. var items = str.split(' ');
  242. if (items.length != 5) _self.error(_self.getText('error2'));
  243. if(mask == '*****') { // 1 possibility
  244. _selectorPeriod.setValue('minute');
  245. }
  246. else if(mask == '-****') { // 1 possibility
  247. _selectorPeriod.setValue('hour');
  248. _selectorMins.setCronValue(items[0]);
  249. _selectorTimeM.setCronValue(items[0]);
  250. }
  251. else if(mask.substring(2, mask.length) == '***') { // 4 possibilities
  252. _selectorPeriod.setValue('day');
  253. _selectorMins.setCronValue(items[0]);
  254. _selectorTimeM.setCronValue(items[0]);
  255. _selectorTimeH.setCronValue(items[1]);
  256. }
  257. else if(mask.substring(2, mask.length) == '-**') { // 4 possibilities
  258. _selectorPeriod.setValue('month');
  259. _selectorMins.setCronValue(items[0]);
  260. _selectorTimeM.setCronValue(items[0]);
  261. _selectorTimeH.setCronValue(items[1]);
  262. _selectorDom.setCronValue(items[2]);
  263. }
  264. else if(mask.substring(2, mask.length) == '**-') { // 4 possibilities
  265. _selectorPeriod.setValue('week');
  266. _selectorMins.setCronValue(items[0]);
  267. _selectorTimeM.setCronValue(items[0]);
  268. _selectorTimeH.setCronValue(items[1]);
  269. _selectorDow.setCronValue(items[4]);
  270. }
  271. else if (mask.substring(3, mask.length) == '-*') { // 8 possibilities
  272. _selectorPeriod.setValue('year');
  273. _selectorMins.setCronValue(items[0]);
  274. _selectorTimeM.setCronValue(items[0]);
  275. _selectorTimeH.setCronValue(items[1]);
  276. _selectorDom.setCronValue(items[2]);
  277. _selectorMonth.setCronValue(items[3]);
  278. }
  279. else {
  280. _self.error(_self.getText('error4'));
  281. }
  282. _self.clearError();
  283. } catch(e) {}
  284. };
  285. // close all child selectors
  286. this.closeSelectors = function(){
  287. for(var n = _selectors.length; n--; ){
  288. _selectors[n].close();
  289. }
  290. };
  291. // get the main element id
  292. this.getId = function(){
  293. return _$elt.attr('id');
  294. }
  295. // get the translated text
  296. this.getText = function(key) {
  297. var text = settings.texts[settings.lang][key] || null;
  298. if(typeof(text) == "string" && text.match('<b')){
  299. text = text.replace(/(<b *\/>)/gi, '</span><b /><span class="jqCron-text">');
  300. text = '<span class="jqCron-text">' + text + '</span>';
  301. }
  302. return text;
  303. };
  304. // get the human readable text
  305. this.getHumanText = function() {
  306. var texts=[];
  307. _$obj
  308. .find('> span > span:visible')
  309. .find('.jqCron-text, .jqCron-selector > span')
  310. .each(function() {
  311. var text = $(this).text().replace(/\s+$/g, '').replace(/^\s+/g, '');
  312. text && texts.push(text);
  313. });
  314. return texts.join(' ').replace(/\s:\s/g, ':');
  315. }
  316. // get settings
  317. this.getSettings = function(){
  318. return settings;
  319. };
  320. // display an error
  321. this.error = function(msg) {
  322. console && console.error('[jqCron Error] ' + msg);
  323. _$obj.addClass('jqCron-error').attr('title', msg);
  324. throw msg;
  325. };
  326. // clear error
  327. this.clearError = function(){
  328. _$obj.attr('title', '').removeClass('jqCron-error');
  329. };
  330. // clear
  331. this.clear = function() {
  332. _selectorDom.setValue([]);
  333. _selectorDow.setValue([]);
  334. _selectorMins.setValue([]);
  335. _selectorMonth.setValue([]);
  336. _selectorTimeH.setValue([]);
  337. _selectorTimeM.setValue([]);
  338. _self.triggerChange();
  339. };
  340. // init (called in constructor)
  341. this.init = function(){
  342. var n,i,labelsList,list;
  343. if(_initialized) return;
  344. settings = jqCronMergeSettings(settings);
  345. settings.jquery_element || _self.error(_self.getText('error3'));
  346. _$elt = settings.jquery_element;
  347. _$elt.append(_$obj);
  348. _$obj.data('id', settings.id);
  349. _$obj.data('jqCron', _self);
  350. _$obj.append(_$blocks);
  351. settings.no_reset_button || _$obj.append(_$cross);
  352. (!settings.disable) || _$obj.addClass('disable');
  353. _$blocks.append(_$blockPERIOD);
  354. if ( /^(ko)$/i.test(settings.lang) )
  355. {
  356. _$blocks.append(_$blockMONTH, _$blockDOM);
  357. }
  358. else
  359. {
  360. _$blocks.append(_$blockDOM, _$blockMONTH);
  361. }
  362. _$blocks.append(_$blockMINS);
  363. _$blocks.append(_$blockDOW);
  364. _$blocks.append(_$blockTIME);
  365. // various binding
  366. _$cross.click(function(){
  367. _self.isDisabled() || _self.clear();
  368. });
  369. // binding from cron to target
  370. _$obj.bind('cron:change', function(evt, value){
  371. if(!settings.bind_to) return;
  372. settings.bind_method.set && settings.bind_method.set(settings.bind_to, value);
  373. _self.clearError();
  374. });
  375. // PERIOD
  376. _$blockPERIOD.append(_self.getText('text_period'));
  377. _selectorPeriod = newSelector(_$blockPERIOD, false, 'period');
  378. settings.enabled_minute && _selectorPeriod.add('minute', _self.getText('name_minute'));
  379. settings.enabled_hour && _selectorPeriod.add('hour', _self.getText('name_hour'));
  380. settings.enabled_day && _selectorPeriod.add('day', _self.getText('name_day'));
  381. settings.enabled_week && _selectorPeriod.add('week', _self.getText('name_week'));
  382. settings.enabled_month && _selectorPeriod.add('month', _self.getText('name_month'));
  383. settings.enabled_year && _selectorPeriod.add('year', _self.getText('name_year'));
  384. _selectorPeriod.$.bind('selector:change', function(e, value){
  385. _$blockDOM.hide();
  386. _$blockMONTH.hide();
  387. _$blockMINS.hide();
  388. _$blockDOW.hide();
  389. _$blockTIME.hide();
  390. if(value == 'hour') {
  391. _$blockMINS.show();
  392. }
  393. else if(value == 'day') {
  394. _$blockTIME.show();
  395. }
  396. else if(value == 'week') {
  397. _$blockDOW.show();
  398. _$blockTIME.show();
  399. }
  400. else if(value == 'month') {
  401. _$blockDOM.show();
  402. _$blockTIME.show();
  403. }
  404. else if(value == 'year') {
  405. _$blockDOM.show();
  406. _$blockMONTH.show();
  407. _$blockTIME.show();
  408. }
  409. });
  410. _selectorPeriod.setValue(settings.default_period);
  411. // MINS (minutes)
  412. _$blockMINS.append(_self.getText('text_mins'));
  413. _selectorMins = newSelector(_$blockMINS, settings.multiple_mins, 'minutes');
  414. for(i=0, list=settings.minutes; i<list.length; i++){
  415. _selectorMins.add(list[i], list[i]);
  416. }
  417. // TIME (hour:min)
  418. _$blockTIME.append(_self.getText('text_time'));
  419. _selectorTimeH = newSelector(_$blockTIME, settings.multiple_time_hours, 'time_hours');
  420. for(i=0, list=settings.hours, labelsList=settings.hour_labels; i<list.length; i++){
  421. _selectorTimeH.add(list[i], labelsList[i]);
  422. }
  423. _selectorTimeM = newSelector(_$blockTIME, settings.multiple_time_minutes, 'time_minutes');
  424. for(i=0, list=settings.minutes; i<list.length; i++){
  425. _selectorTimeM.add(list[i], list[i]);
  426. }
  427. // DOW (day of week)
  428. _$blockDOW.append(_self.getText('text_dow'));
  429. _selectorDow = newSelector(_$blockDOW, settings.multiple_dow, 'day_of_week');
  430. for(i=0, list=_self.getText('weekdays'); i<list.length; i++){
  431. _selectorDow.add(i+1, list[i]);
  432. }
  433. // DOM (day of month)
  434. _$blockDOM.append(_self.getText('text_dom'));
  435. _selectorDom = newSelector(_$blockDOM, settings.multiple_dom, 'day_of_month');
  436. for(i=0, list=settings.monthdays; i<list.length; i++){
  437. _selectorDom.add(list[i], list[i]);
  438. }
  439. // MONTH (day of week)
  440. _$blockMONTH.append(_self.getText('text_month'));
  441. _selectorMonth = newSelector(_$blockMONTH, settings.multiple_month, 'month');
  442. for(i=0, list=_self.getText('months'); i<list.length; i++){
  443. _selectorMonth.add(i+1, list[i]);
  444. }
  445. // close all selectors when we click in body
  446. $('body').click(function(){
  447. var i, n = _selectors.length;
  448. for(i = 0; i < n; i++){
  449. _selectors[i].close();
  450. }
  451. });
  452. _initialized = true;
  453. // default value
  454. if(settings.default_value) {
  455. _self.setCron(settings.default_value);
  456. }
  457. };
  458. // trigger a change event
  459. this.triggerChange = function(){
  460. _$obj.trigger('cron:change', _self.getCron());
  461. };
  462. // store instance in array
  463. jqCronInstances.push(this);
  464. // expose main jquery object
  465. this.$ = _$obj;
  466. // init
  467. try {
  468. this.init();
  469. _self.triggerChange();
  470. } catch(e){}
  471. }
  472. this.jqCron = jqCron;
  473. }).call(window, $);
  474. /**
  475. * jqCronSelector class
  476. */
  477. (function($){
  478. function jqCronSelector(_cron, _$block, _multiple, _type){
  479. var _self = this;
  480. var _$list = $('<ul class="jqCron-selector-list"></ul>');
  481. var _$title = $('<span class="jqCron-selector-title"></span>');
  482. var _$selector = $('<span class="jqCron-selector"></span>');
  483. var _values = {};
  484. var _value = [];
  485. var _hasNumericTexts = true;
  486. var _numeric_zero_pad = _cron.getSettings().numeric_zero_pad;
  487. // return an array without doublon
  488. function array_unique(l){
  489. var i=0,n=l.length,k={},a=[];
  490. while(i<n) {
  491. k[l[i]] || (k[l[i]] = 1 && a.push(l[i]));
  492. i++;
  493. }
  494. return a;
  495. }
  496. // get the value (an array if multiple, else a single value)
  497. this.getValue = function(){
  498. return _multiple ? _value : _value[0];
  499. };
  500. // get a correct string for cron
  501. this.getCronValue = function(){
  502. if(_value.length == 0) return '*';
  503. var cron = [_value[0]], i, s = _value[0], c = _value[0], n = _value.length;
  504. for(i=1; i<n; i++) {
  505. if(_value[i] == c+1) {
  506. c = _value[i];
  507. cron[cron.length-1] = s+'-'+c;
  508. }
  509. else {
  510. s = c = _value[i];
  511. cron.push(c);
  512. }
  513. }
  514. return cron.join(',');
  515. };
  516. // set the cron value
  517. this.setCronValue = function(str) {
  518. var values = [], m ,i, n;
  519. if(str !== '*') {
  520. while(str != '') {
  521. // test "*/n" expression
  522. m = str.match(/^\*\/([0-9]+),?/);
  523. if(m && m.length == 2) {
  524. for(i=0; i<=59; i+=(m[1]|0)) {
  525. values.push(i);
  526. }
  527. str = str.replace(m[0], '');
  528. continue;
  529. }
  530. // test "a-b/n" expression
  531. m = str.match(/^([0-9]+)-([0-9]+)\/([0-9]+),?/);
  532. if(m && m.length == 4) {
  533. for(i=(m[1]|0); i<=(m[2]|0); i+=(m[3]|0)) {
  534. values.push(i);
  535. }
  536. str = str.replace(m[0], '');
  537. continue;
  538. }
  539. // test "a-b" expression
  540. m = str.match(/^([0-9]+)-([0-9]+),?/);
  541. if(m && m.length == 3) {
  542. for(i=(m[1]|0); i<=(m[2]|0); i++) {
  543. values.push(i);
  544. }
  545. str = str.replace(m[0], '');
  546. continue;
  547. }
  548. // test "c" expression
  549. m = str.match(/^([0-9]+),?/);
  550. if(m && m.length == 2) {
  551. values.push(m[1]|0);
  552. str = str.replace(m[0], '');
  553. continue;
  554. }
  555. // something goes wrong in the expression
  556. return ;
  557. }
  558. }
  559. _self.setValue(values);
  560. };
  561. // close the selector
  562. this.close = function(){
  563. _$selector.trigger('selector:close');
  564. };
  565. // open the selector
  566. this.open = function(){
  567. _$selector.trigger('selector:open');
  568. };
  569. // whether the selector is open
  570. this.isOpened = function() {
  571. return _$list.is(':visible');
  572. };
  573. // add a selected value to the list
  574. this.addValue = function(key) {
  575. var values = _multiple ? _value.slice(0) : []; // clone array
  576. values.push(key);
  577. _self.setValue(values);
  578. };
  579. // remove a selected value from the list
  580. this.removeValue = function(key) {
  581. if(_multiple) {
  582. var i, newValue = [];
  583. for(i=0; i<_value.length; i++){
  584. if(key != [_value[i]]) {
  585. newValue.push(_value[i]);
  586. }
  587. }
  588. _self.setValue(newValue);
  589. }
  590. else {
  591. _self.clear();
  592. }
  593. };
  594. // set the selected value(s) of the list
  595. this.setValue = function(keys){
  596. var i, newKeys = [], saved = _value.join(' ');
  597. if(!$.isArray(keys)) keys = [keys];
  598. _$list.find('li').removeClass('selected');
  599. keys = array_unique(keys);
  600. keys.sort(function(a, b){
  601. var ta = typeof(a);
  602. var tb = typeof(b);
  603. if(ta==tb && ta=="number") return a-b;
  604. else return String(a) == String(b) ? 0 : (String(a) < String(b) ? -1 : 1);
  605. });
  606. if(_multiple) {
  607. for(i=0; i<keys.length; i++){
  608. if(keys[i] in _values) {
  609. _values[keys[i]].addClass('selected');
  610. newKeys.push(keys[i]);
  611. }
  612. }
  613. }
  614. else {
  615. if(keys[0] in _values) {
  616. _values[keys[0]].addClass('selected');
  617. newKeys.push(keys[0]);
  618. }
  619. }
  620. // remove unallowed values
  621. _value = newKeys;
  622. if(saved != _value.join(' ')) {
  623. _$selector.trigger('selector:change', _multiple ? keys : keys[0]);
  624. return true;
  625. }
  626. return false;
  627. };
  628. // get the title text
  629. this.getTitleText = function(){
  630. var getValueText = function(key) {
  631. return (key in _values) ? _values[key].text() : key;
  632. };
  633. if(_value.length == 0) {
  634. return _cron.getText('empty_' + _type) || _cron.getText('empty');
  635. }
  636. var cron = [getValueText(_value[0])], i, s = _value[0], c = _value[0], n = _value.length;
  637. for(i=1; i<n; i++) {
  638. if(_value[i] == c+1) {
  639. c = _value[i];
  640. cron[cron.length-1] = getValueText(s)+'-'+getValueText(c);
  641. }
  642. else {
  643. s = c = _value[i];
  644. cron.push(getValueText(c));
  645. }
  646. }
  647. return cron.join(',');
  648. };
  649. // clear list
  650. this.clear = function() {
  651. _values = {};
  652. _self.setValue([]);
  653. _$list.empty();
  654. };
  655. // add a (key, value) pair
  656. this.add = function(key, value) {
  657. if(!(value+'').match(/^[0-9]+$/)) _hasNumericTexts = false;
  658. if(_numeric_zero_pad && _hasNumericTexts && value < 10) {
  659. value = '0'+value;
  660. }
  661. var $item = $('<li>' + value + '</li>');
  662. _$list.append($item);
  663. _values[key] = $item;
  664. $item.click(function(){
  665. if(_multiple && $(this).hasClass('selected')) {
  666. _self.removeValue(key);
  667. }
  668. else {
  669. _self.addValue(key);
  670. if(!_multiple) _self.close();
  671. }
  672. });
  673. };
  674. // expose main jquery object
  675. this.$ = _$selector;
  676. // constructor
  677. _$block.find('b:eq(0)').after(_$selector).remove();
  678. _$selector
  679. .addClass('jqCron-selector-' + _$block.find('.jqCron-selector').length)
  680. .append(_$title)
  681. .append(_$list)
  682. .bind('selector:open', function(){
  683. if(_hasNumericTexts) {
  684. var nbcols = 1, n = _$list.find('li').length;
  685. if(n > 5 && n <= 16) nbcols = 2;
  686. else if(n > 16 && n <= 23) nbcols = 3;
  687. else if(n > 23 && n <= 40) nbcols = 4;
  688. else if(n > 40) nbcols = 5;
  689. _$list.addClass('cols'+nbcols);
  690. }
  691. _$list.show();
  692. })
  693. .bind('selector:close', function(){
  694. _$list.hide();
  695. })
  696. .bind('selector:change', function(){
  697. _$title.html(_self.getTitleText());
  698. })
  699. .click(function(e){
  700. e.stopPropagation();
  701. })
  702. .trigger('selector:change')
  703. ;
  704. $.fn.disableSelection && _$selector.disableSelection(); // only work with jQuery UI
  705. _$title.click(function(e){
  706. (_self.isOpened() || _cron.isDisabled()) ? _self.close() : _self.open();
  707. });
  708. _self.close();
  709. _self.clear();
  710. }
  711. this.jqCronSelector = jqCronSelector;
  712. }).call(window, $);
  713. /**
  714. * Generate unique id for each element.
  715. * Skip elements which have already an id.
  716. */
  717. (function($){
  718. var jqUID = 0;
  719. var jqGetUID = function(prefix){
  720. var id;
  721. while(1) {
  722. jqUID++;
  723. id = ((prefix || 'JQUID')+'') + jqUID;
  724. if(!document.getElementById(id)) return id;
  725. }
  726. };
  727. $.fn.uniqueId = function(prefix) {
  728. return this.each(function(){
  729. if($(this).attr('id')) return;
  730. var id = jqGetUID(prefix);
  731. $(this).attr('id', id);
  732. });
  733. };
  734. }).call(window, $);
  735. /**
  736. * Extends jQuery selectors with new block selector
  737. */
  738. (function($){
  739. $.extend($.expr[':'], {
  740. container: function(a) {
  741. return (a.tagName+'').toLowerCase() in {
  742. a:1,
  743. abbr:1,
  744. acronym:1,
  745. address:1,
  746. b:1,
  747. big:1,
  748. blockquote:1,
  749. button:1,
  750. cite:1,
  751. code:1,
  752. dd: 1,
  753. del:1,
  754. dfn:1,
  755. div:1,
  756. dt:1,
  757. em:1,
  758. fieldset:1,
  759. form:1,
  760. h1:1,
  761. h2:1,
  762. h3:1,
  763. h4:1,
  764. h5:1,
  765. h6: 1,
  766. i:1,
  767. ins:1,
  768. kbd:1,
  769. label:1,
  770. li:1,
  771. p:1,
  772. pre:1,
  773. q:1,
  774. samp:1,
  775. small:1,
  776. span:1,
  777. strong:1,
  778. sub: 1,
  779. sup:1,
  780. td:1,
  781. tt:1
  782. };
  783. },
  784. autoclose: function(a) {
  785. return (a.tagName+'').toLowerCase() in {
  786. area:1,
  787. base:1,
  788. basefont:1,
  789. br:1,
  790. col:1,
  791. frame:1,
  792. hr:1,
  793. img:1,
  794. input:1,
  795. link:1,
  796. meta:1,
  797. param:1
  798. };
  799. }
  800. });
  801. }).call(window, $);