jqCron.js 25 KB

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