/* * # Fomantic UI - 2.9.0 * https://github.com/fomantic/Fomantic-UI * http://fomantic-ui.com/ * * Copyright 2022 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT * */ /*! * # Fomantic-UI 2.9.0 - API * http://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * http://opensource.org/licenses/MIT * */ ;(function ($, window, document, undefined) { 'use strict'; $.isWindow = $.isWindow || function(obj) { return obj != null && obj === obj.window; }; window = (typeof window != 'undefined' && window.Math == Math) ? window : (typeof self != 'undefined' && self.Math == Math) ? self : Function('return this')() ; $.api = $.fn.api = function(parameters) { var // use window context if none specified $allModules = $.isFunction(this) ? $(window) : $(this), moduleSelector = $allModules.selector || '', time = new Date().getTime(), performance = [], query = arguments[0], methodInvoked = (typeof query == 'string'), queryArguments = [].slice.call(arguments, 1), returnedValue ; $allModules .each(function() { var settings = ( $.isPlainObject(parameters) ) ? $.extend(true, {}, $.fn.api.settings, parameters) : $.extend({}, $.fn.api.settings), // internal aliases namespace = settings.namespace, metadata = settings.metadata, selector = settings.selector, error = settings.error, className = settings.className, // define namespaces for modules eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, // element that creates request $module = $(this), $form = $module.closest(selector.form), // context used for state $context = (settings.stateContext) ? ([window,document].indexOf(settings.stateContext) < 0 ? $(document).find(settings.stateContext) : $(settings.stateContext)) : $module, // request details ajaxSettings, requestSettings, url, data, requestStartTime, originalData, // standard module element = this, context = $context[0], instance = $module.data(moduleNamespace), module ; module = { initialize: function() { if(!methodInvoked) { originalData = settings.data; module.bind.events(); } module.instantiate(); }, instantiate: function() { module.verbose('Storing instance of module', module); instance = module; $module .data(moduleNamespace, instance) ; }, destroy: function() { module.verbose('Destroying previous module for', element); $module .removeData(moduleNamespace) .off(eventNamespace) ; }, bind: { events: function() { var triggerEvent = module.get.event() ; if( triggerEvent ) { module.verbose('Attaching API events to element', triggerEvent); $module .on(triggerEvent + eventNamespace, module.event.trigger) ; } else if(settings.on == 'now') { module.debug('Querying API endpoint immediately'); module.query(); } } }, decode: { json: function(response) { if(response !== undefined && typeof response == 'string') { try { response = JSON.parse(response); } catch(e) { // isn't json string } } return response; } }, read: { cachedResponse: function(url) { var response ; if(window.Storage === undefined) { module.error(error.noStorage); return; } response = sessionStorage.getItem(url + module.get.normalizedData()); module.debug('Using cached response', url, settings.data, response); response = module.decode.json(response); return response; } }, write: { cachedResponse: function(url, response) { if(response && response === '') { module.debug('Response empty, not caching', response); return; } if(window.Storage === undefined) { module.error(error.noStorage); return; } if( $.isPlainObject(response) ) { response = JSON.stringify(response); } sessionStorage.setItem(url + module.get.normalizedData(), response); module.verbose('Storing cached response for url', url, settings.data, response); } }, query: function() { if(module.is.disabled()) { module.debug('Element is disabled API request aborted'); return; } if(module.is.loading()) { if(settings.interruptRequests) { module.debug('Interrupting previous request'); module.abort(); } else { module.debug('Cancelling request, previous request is still pending'); return; } } // pass element metadata to url (value, text) if(settings.defaultData) { $.extend(true, settings.urlData, module.get.defaultData()); } // Add form content if(settings.serializeForm) { settings.data = module.add.formData(originalData || settings.data); } // call beforesend and get any settings changes requestSettings = module.get.settings(); // check if before send cancelled request if(requestSettings === false) { module.cancelled = true; module.error(error.beforeSend); return; } else { module.cancelled = false; } // get url url = module.get.templatedURL(); if(!url && !module.is.mocked()) { module.error(error.missingURL); return; } // replace variables url = module.add.urlData( url ); // missing url parameters if( !url && !module.is.mocked()) { return; } requestSettings.url = settings.base + url; // look for jQuery ajax parameters in settings ajaxSettings = $.extend(true, {}, settings, { type : settings.method || settings.type, data : data, url : settings.base + url, beforeSend : settings.beforeXHR, success : function() {}, failure : function() {}, complete : function() {} }); module.debug('Querying URL', ajaxSettings.url); module.verbose('Using AJAX settings', ajaxSettings); if(settings.cache === 'local' && module.read.cachedResponse(url)) { module.debug('Response returned from local cache'); module.request = module.create.request(); module.request.resolveWith(context, [ module.read.cachedResponse(url) ]); return; } if( !settings.throttle ) { module.debug('Sending request', data, ajaxSettings.method); module.send.request(); } else { if(!settings.throttleFirstRequest && !module.timer) { module.debug('Sending request', data, ajaxSettings.method); module.send.request(); module.timer = setTimeout(function(){}, settings.throttle); } else { module.debug('Throttling request', settings.throttle); clearTimeout(module.timer); module.timer = setTimeout(function() { if(module.timer) { delete module.timer; } module.debug('Sending throttled request', data, ajaxSettings.method); module.send.request(); }, settings.throttle); } } }, should: { removeError: function() { return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) ); } }, is: { disabled: function() { return ($module.filter(selector.disabled).length > 0); }, expectingJSON: function() { return settings.dataType === 'json' || settings.dataType === 'jsonp'; }, form: function() { return $module.is('form') || $context.is('form'); }, mocked: function() { return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync); }, input: function() { return $module.is('input'); }, loading: function() { return (module.request) ? (module.request.state() == 'pending') : false ; }, abortedRequest: function(xhr) { if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) { module.verbose('XHR request determined to be aborted'); return true; } else { module.verbose('XHR request was not aborted'); return false; } }, validResponse: function(response) { if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) { module.verbose('Response is not JSON, skipping validation', settings.successTest, response); return true; } module.debug('Checking JSON returned success', settings.successTest, response); if( settings.successTest(response) ) { module.debug('Response passed success test', response); return true; } else { module.debug('Response failed success test', response); return false; } } }, was: { cancelled: function() { return (module.cancelled || false); }, successful: function() { return (module.request && module.request.state() == 'resolved'); }, failure: function() { return (module.request && module.request.state() == 'rejected'); }, complete: function() { return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') ); } }, add: { urlData: function(url, urlData) { var requiredVariables, optionalVariables ; if(url) { requiredVariables = url.match(settings.regExp.required); optionalVariables = url.match(settings.regExp.optional); urlData = urlData || settings.urlData; if(requiredVariables) { module.debug('Looking for required URL variables', requiredVariables); $.each(requiredVariables, function(index, templatedString) { var // allow legacy {$var} style variable = (templatedString.indexOf('$') !== -1) ? templatedString.slice(2, -1) : templatedString.slice(1, -1), value = ($.isPlainObject(urlData) && urlData[variable] !== undefined) ? urlData[variable] : ($module.data(variable) !== undefined) ? $module.data(variable) : ($context.data(variable) !== undefined) ? $context.data(variable) : urlData[variable] ; // remove value if(value === undefined) { module.error(error.requiredParameter, variable, url); url = false; return false; } else { module.verbose('Found required variable', variable, value); value = (settings.encodeParameters) ? module.get.urlEncodedValue(value) : value ; url = url.replace(templatedString, value); } }); } if(optionalVariables) { module.debug('Looking for optional URL variables', requiredVariables); $.each(optionalVariables, function(index, templatedString) { var // allow legacy {/$var} style variable = (templatedString.indexOf('$') !== -1) ? templatedString.slice(3, -1) : templatedString.slice(2, -1), value = ($.isPlainObject(urlData) && urlData[variable] !== undefined) ? urlData[variable] : ($module.data(variable) !== undefined) ? $module.data(variable) : ($context.data(variable) !== undefined) ? $context.data(variable) : urlData[variable] ; // optional replacement if(value !== undefined) { module.verbose('Optional variable Found', variable, value); url = url.replace(templatedString, value); } else { module.verbose('Optional variable not found', variable); // remove preceding slash if set if(url.indexOf('/' + templatedString) !== -1) { url = url.replace('/' + templatedString, ''); } else { url = url.replace(templatedString, ''); } } }); } } return url; }, formData: function(data) { var formData = {}, hasOtherData, useFormDataApi = settings.serializeForm === 'formdata' ; data = data || originalData || settings.data; hasOtherData = $.isPlainObject(data); if (useFormDataApi) { formData = new FormData($form[0]); settings.processData = typeof settings.processData !== 'undefined' ? settings.processData : false; settings.contentType = typeof settings.contentType !== 'undefined' ? settings.contentType : false; } else { var formArray = $form.serializeArray(), pushes = {}, pushValues= {}, build = function(base, key, value) { base[key] = value; return base; } ; // add files $.each($('input[type="file"]',$form), function(i, tag) { $.each($(tag)[0].files, function(j, file) { formArray.push({name:tag.name, value: file}); }); }); $.each(formArray, function(i, el) { if (!settings.regExp.validate.test(el.name)) return; var isCheckbox = $('[name="' + el.name + '"]', $form).attr('type') === 'checkbox', floatValue = parseFloat(el.value), value = (isCheckbox && el.value === 'on') || el.value === 'true' || (String(floatValue) === el.value ? floatValue : (el.value === 'false' ? false : el.value)), nameKeys = el.name.match(settings.regExp.key) || [], k, pushKey= el.name.replace(/\[\]$/,'') ; if(!(pushKey in pushes)) { pushes[pushKey] = 0; pushValues[pushKey] = value; } else if (Array.isArray(pushValues[pushKey])) { pushValues[pushKey].push(value); } else { pushValues[pushKey] = [pushValues[pushKey] , value]; } value = pushValues[pushKey]; while ((k = nameKeys.pop()) !== undefined) { // foo[] if (k == '' && !Array.isArray(value)){ value = build([], pushes[pushKey]++, value); } // foo[n] else if (settings.regExp.fixed.test(k)) { value = build([], k, value); } // foo; foo[bar] else if (settings.regExp.named.test(k)) { value = build({}, k, value); } } formData = $.extend(true, formData, value); }); } if(hasOtherData) { module.debug('Extending existing data with form data', data, formData); if(useFormDataApi) { $.each(Object.keys(data),function(i, el){ formData.append(el, data[el]); }); data = formData; } else { data = $.extend(true, {}, data, formData); } } else { module.debug('Adding form data', formData); data = formData; } return data; } }, send: { request: function() { module.set.loading(); module.request = module.create.request(); if( module.is.mocked() ) { module.mockedXHR = module.create.mockedXHR(); } else { module.xhr = module.create.xhr(); } settings.onRequest.call(context, module.request, module.xhr); } }, event: { trigger: function(event) { module.query(); if(event.type == 'submit' || event.type == 'click') { event.preventDefault(); } }, xhr: { always: function() { // nothing special }, done: function(response, textStatus, xhr) { var context = this, elapsedTime = (new Date().getTime() - requestStartTime), timeLeft = (settings.loadingDuration - elapsedTime), translatedResponse = ( $.isFunction(settings.onResponse) ) ? module.is.expectingJSON() && !settings.rawResponse ? settings.onResponse.call(context, $.extend(true, {}, response)) : settings.onResponse.call(context, response) : false ; timeLeft = (timeLeft > 0) ? timeLeft : 0 ; if(translatedResponse) { module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response); response = translatedResponse; } if(timeLeft > 0) { module.debug('Response completed early delaying state change by', timeLeft); } setTimeout(function() { if( module.is.validResponse(response) ) { module.request.resolveWith(context, [response, xhr]); } else { module.request.rejectWith(context, [xhr, 'invalid']); } }, timeLeft); }, fail: function(xhr, status, httpMessage) { var context = this, elapsedTime = (new Date().getTime() - requestStartTime), timeLeft = (settings.loadingDuration - elapsedTime) ; timeLeft = (timeLeft > 0) ? timeLeft : 0 ; if(timeLeft > 0) { module.debug('Response completed early delaying state change by', timeLeft); } setTimeout(function() { if( module.is.abortedRequest(xhr) ) { module.request.rejectWith(context, [xhr, 'aborted', httpMessage]); } else { module.request.rejectWith(context, [xhr, 'error', status, httpMessage]); } }, timeLeft); } }, request: { done: function(response, xhr) { module.debug('Successful API Response', response); if(settings.cache === 'local' && url) { module.write.cachedResponse(url, response); module.debug('Saving server response locally', module.cache); } settings.onSuccess.call(context, response, $module, xhr); }, complete: function(firstParameter, secondParameter) { var xhr, response ; // have to guess callback parameters based on request success if( module.was.successful() ) { response = firstParameter; xhr = secondParameter; } else { xhr = firstParameter; response = module.get.responseFromXHR(xhr); } module.remove.loading(); settings.onComplete.call(context, response, $module, xhr); }, fail: function(xhr, status, httpMessage) { var // pull response from xhr if available response = module.get.responseFromXHR(xhr), errorMessage = module.get.errorFromRequest(response, status, httpMessage) ; if(status == 'aborted') { module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage); settings.onAbort.call(context, status, $module, xhr); return true; } else if(status == 'invalid') { module.debug('JSON did not pass success test. A server-side error has most likely occurred', response); } else if(status == 'error') { if(xhr !== undefined) { module.debug('XHR produced a server error', status, httpMessage); // make sure we have an error to display to console if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') { module.error(error.statusMessage + httpMessage, ajaxSettings.url); } settings.onError.call(context, errorMessage, $module, xhr); } } if(settings.errorDuration && status !== 'aborted') { module.debug('Adding error state'); module.set.error(); if( module.should.removeError() ) { setTimeout(module.remove.error, settings.errorDuration); } } module.debug('API Request failed', errorMessage, xhr); settings.onFailure.call(context, response, $module, xhr); } } }, create: { request: function() { // api request promise return $.Deferred() .always(module.event.request.complete) .done(module.event.request.done) .fail(module.event.request.fail) ; }, mockedXHR: function () { var // xhr does not simulate these properties of xhr but must return them textStatus = false, status = false, httpMessage = false, responder = settings.mockResponse || settings.response, asyncResponder = settings.mockResponseAsync || settings.responseAsync, asyncCallback, response, mockedXHR ; mockedXHR = $.Deferred() .always(module.event.xhr.complete) .done(module.event.xhr.done) .fail(module.event.xhr.fail) ; if(responder) { if( $.isFunction(responder) ) { module.debug('Using specified synchronous callback', responder); response = responder.call(context, requestSettings); } else { module.debug('Using settings specified response', responder); response = responder; } // simulating response mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]); } else if( $.isFunction(asyncResponder) ) { asyncCallback = function(response) { module.debug('Async callback returned response', response); if(response) { mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]); } else { mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]); } }; module.debug('Using specified async response callback', asyncResponder); asyncResponder.call(context, requestSettings, asyncCallback); } return mockedXHR; }, xhr: function() { var xhr ; // ajax request promise xhr = $.ajax(ajaxSettings) .always(module.event.xhr.always) .done(module.event.xhr.done) .fail(module.event.xhr.fail) ; module.verbose('Created server request', xhr, ajaxSettings); return xhr; } }, set: { error: function() { module.verbose('Adding error state to element', $context); $context.addClass(className.error); }, loading: function() { module.verbose('Adding loading state to element', $context); $context.addClass(className.loading); requestStartTime = new Date().getTime(); } }, remove: { error: function() { module.verbose('Removing error state from element', $context); $context.removeClass(className.error); }, loading: function() { module.verbose('Removing loading state from element', $context); $context.removeClass(className.loading); } }, get: { normalizedData: function(){ return typeof settings.data === "string" ? settings.data : JSON.stringify(settings.data, Object.keys(settings.data).sort()); }, responseFromXHR: function(xhr) { return $.isPlainObject(xhr) ? (module.is.expectingJSON()) ? module.decode.json(xhr.responseText) : xhr.responseText : false ; }, errorFromRequest: function(response, status, httpMessage) { return ($.isPlainObject(response) && response.error !== undefined) ? response.error // use json error message : (settings.error[status] !== undefined) // use server error message ? settings.error[status] : httpMessage ; }, request: function() { return module.request || false; }, xhr: function() { return module.xhr || false; }, settings: function() { var runSettings ; runSettings = settings.beforeSend.call($module, settings); if(runSettings) { if(runSettings.success !== undefined) { module.debug('Legacy success callback detected', runSettings); module.error(error.legacyParameters, runSettings.success); runSettings.onSuccess = runSettings.success; } if(runSettings.failure !== undefined) { module.debug('Legacy failure callback detected', runSettings); module.error(error.legacyParameters, runSettings.failure); runSettings.onFailure = runSettings.failure; } if(runSettings.complete !== undefined) { module.debug('Legacy complete callback detected', runSettings); module.error(error.legacyParameters, runSettings.complete); runSettings.onComplete = runSettings.complete; } } if(runSettings === undefined) { module.error(error.noReturnedValue); } if(runSettings === false) { return runSettings; } return (runSettings !== undefined) ? $.extend(true, {}, runSettings) : $.extend(true, {}, settings) ; }, urlEncodedValue: function(value) { var decodedValue = window.decodeURIComponent(value), encodedValue = window.encodeURIComponent(value), alreadyEncoded = (decodedValue !== value) ; if(alreadyEncoded) { module.debug('URL value is already encoded, avoiding double encoding', value); return value; } module.verbose('Encoding value using encodeURIComponent', value, encodedValue); return encodedValue; }, defaultData: function() { var data = {} ; if( !$.isWindow(element) ) { if( module.is.input() ) { data.value = $module.val(); } else if( module.is.form() ) { } else { data.text = $module.text(); } } return data; }, event: function() { if( $.isWindow(element) || settings.on == 'now' ) { module.debug('API called without element, no events attached'); return false; } else if(settings.on == 'auto') { if( $module.is('input') ) { return (element.oninput !== undefined) ? 'input' : (element.onpropertychange !== undefined) ? 'propertychange' : 'keyup' ; } else if( $module.is('form') ) { return 'submit'; } else { return 'click'; } } else { return settings.on; } }, templatedURL: function(action) { action = action || $module.data(metadata.action) || settings.action || false; url = $module.data(metadata.url) || settings.url || false; if(url) { module.debug('Using specified url', url); return url; } if(action) { module.debug('Looking up url for action', action, settings.api); if(settings.api[action] === undefined && !module.is.mocked()) { module.error(error.missingAction, settings.action, settings.api); return; } url = settings.api[action]; } else if( module.is.form() ) { url = $module.attr('action') || $context.attr('action') || false; module.debug('No url or action specified, defaulting to form action', url); } return url; } }, abort: function() { var xhr = module.get.xhr() ; if( xhr && xhr.state() !== 'resolved') { module.debug('Cancelling API request'); xhr.abort(); } }, // reset state reset: function() { module.remove.error(); module.remove.loading(); }, setting: function(name, value) { module.debug('Changing setting', name, value); if( $.isPlainObject(name) ) { $.extend(true, settings, name); } else if(value !== undefined) { if($.isPlainObject(settings[name])) { $.extend(true, settings[name], value); } else { settings[name] = value; } } else { return settings[name]; } }, internal: function(name, value) { if( $.isPlainObject(name) ) { $.extend(true, module, name); } else if(value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function() { if(!settings.silent && settings.debug) { if(settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function() { if(!settings.silent && settings.verbose && settings.debug) { if(settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function() { if(!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function(message) { var currentTime, executionTime, previousTime ; if(settings.performance) { currentTime = new Date().getTime(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ 'Name' : message[0], 'Arguments' : [].slice.call(message, 1) || '', //'Element' : element, 'Execution Time' : executionTime }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(module.performance.display, 500); }, display: function() { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function(index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if(moduleSelector) { title += ' \'' + moduleSelector + '\''; } if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { console.groupCollapsed(title); if(console.table) { console.table(performance); } else { $.each(performance, function(index, data) { console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); }); } console.groupEnd(); } performance = []; } }, invoke: function(query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if(typeof query == 'string' && object !== undefined) { query = query.split(/[\. ]/); maxDepth = query.length - 1; $.each(query, function(depth, value) { var camelCaseValue = (depth != maxDepth) ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query ; if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { object = object[camelCaseValue]; } else if( object[camelCaseValue] !== undefined ) { found = object[camelCaseValue]; return false; } else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { object = object[value]; } else if( object[value] !== undefined ) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if ( $.isFunction( found ) ) { response = found.apply(context, passedArguments); } else if(found !== undefined) { response = found; } if(Array.isArray(returnedValue)) { returnedValue.push(response); } else if(returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if(response !== undefined) { returnedValue = response; } return found; } }; if(methodInvoked) { if(instance === undefined) { module.initialize(); } module.invoke(query); } else { if(instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } }) ; return (returnedValue !== undefined) ? returnedValue : this ; }; $.api.settings = { name : 'API', namespace : 'api', debug : false, verbose : false, performance : true, // object containing all templates endpoints api : {}, // whether to cache responses cache : true, // whether new requests should abort previous requests interruptRequests : true, // event binding on : 'auto', // context for applying state classes stateContext : false, // duration for loading state loadingDuration : 0, // whether to hide errors after a period of time hideError : 'auto', // duration for error state errorDuration : 2000, // whether parameters should be encoded with encodeURIComponent encodeParameters : true, // API action to use action : false, // templated URL to use url : false, // base URL to apply to all endpoints base : '', // data that will urlData : {}, // whether to add default data to url data defaultData : true, // whether to serialize closest form // use true to convert complex named keys like a[b][1][c][] into a nested object // use 'formdata' for formdata web api serializeForm : false, // how long to wait before request should occur throttle : 0, // whether to throttle first request or only repeated throttleFirstRequest : true, // standard ajax settings method : 'get', data : {}, dataType : 'json', // mock response mockResponse : false, mockResponseAsync : false, // aliases for mock response : false, responseAsync : false, // whether onResponse should work with response value without force converting into an object rawResponse : true, // callbacks before request beforeSend : function(settings) { return settings; }, beforeXHR : function(xhr) {}, onRequest : function(promise, xhr) {}, // after request onResponse : false, // function(response) { }, // response was successful, if JSON passed validation onSuccess : function(response, $module) {}, // request finished without aborting onComplete : function(response, $module) {}, // failed JSON success test onFailure : function(response, $module) {}, // server error onError : function(errorMessage, $module) {}, // request aborted onAbort : function(errorMessage, $module) {}, successTest : false, // errors error : { beforeSend : 'The before send function has aborted the request', error : 'There was an error with your request', exitConditions : 'API Request Aborted. Exit conditions met', JSONParse : 'JSON could not be parsed during error handling', legacyParameters : 'You are using legacy API success callback names', method : 'The method you called is not defined', missingAction : 'API action used but no url was defined', missingURL : 'No URL specified for api event', noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.', noStorage : 'Caching responses locally requires session storage', parseError : 'There was an error parsing your request', requiredParameter : 'Missing a required URL parameter: ', statusMessage : 'Server gave an error: ', timeout : 'Your request timed out' }, regExp : { required : /\{\$*[a-z0-9]+\}/gi, optional : /\{\/\$*[a-z0-9]+\}/gi, validate: /^[a-z_][a-z0-9_-]*(?:\[[a-z0-9_-]*\])*$/i, key: /[a-z0-9_-]+|(?=\[\])/gi, push: /^$/, fixed: /^\d+$/, named: /^[a-z0-9_-]+$/i }, className: { loading : 'loading', error : 'error' }, selector: { disabled : '.disabled', form : 'form' }, metadata: { action : 'action', url : 'url' } }; })( jQuery, window, document ); /*! * # Fomantic-UI 2.9.0 - Calendar * http://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * http://opensource.org/licenses/MIT * */ ;(function ($, window, document, undefined) { 'use strict'; $.isFunction = $.isFunction || function(obj) { return typeof obj === "function" && typeof obj.nodeType !== "number"; }; window = (typeof window != 'undefined' && window.Math == Math) ? window : (typeof self != 'undefined' && self.Math == Math) ? self : Function('return this')() ; $.fn.calendar = function(parameters) { var $allModules = $(this), moduleSelector = $allModules.selector || '', time = new Date().getTime(), performance = [], query = arguments[0], methodInvoked = (typeof query == 'string'), queryArguments = [].slice.call(arguments, 1), returnedValue, timeGapTable = { '5': {'row': 4, 'column': 3 }, '10': {'row': 3, 'column': 2 }, '15': {'row': 2, 'column': 2 }, '20': {'row': 3, 'column': 1 }, '30': {'row': 2, 'column': 1 } }, numberText = ['','one','two','three','four','five','six','seven','eight'] ; $allModules .each(function () { var settings = ( $.isPlainObject(parameters) ) ? $.extend(true, {}, $.fn.calendar.settings, parameters) : $.extend({}, $.fn.calendar.settings), className = settings.className, namespace = settings.namespace, selector = settings.selector, formatter = settings.formatter, parser = settings.parser, metadata = settings.metadata, timeGap = timeGapTable[settings.minTimeGap], error = settings.error, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $input = $module.find(selector.input), $container = $module.find(selector.popup), $activator = $module.find(selector.activator), element = this, instance = $module.data(moduleNamespace), isTouch, isTouchDown = false, isInverted = $module.hasClass(className.inverted), focusDateUsedForRange = false, selectionComplete = false, classObserver, module ; module = { initialize: function () { module.debug('Initializing calendar for', element, $module); isTouch = module.get.isTouch(); module.setup.config(); module.setup.popup(); module.setup.inline(); module.setup.input(); module.setup.date(); module.create.calendar(); module.bind.events(); module.observeChanges(); module.instantiate(); }, instantiate: function () { module.verbose('Storing instance of calendar'); instance = module; $module.data(moduleNamespace, instance); }, destroy: function () { module.verbose('Destroying previous calendar for', element); $module.removeData(moduleNamespace); module.unbind.events(); module.disconnect.classObserver(); }, setup: { config: function () { if (module.get.minDate() !== null) { module.set.minDate($module.data(metadata.minDate)); } if (module.get.maxDate() !== null) { module.set.maxDate($module.data(metadata.maxDate)); } module.setting('type', module.get.type()); module.setting('on', settings.on || 'click'); }, popup: function () { if (settings.inline) { return; } if (!$activator.length) { $activator = $module.children().first(); if (!$activator.length) { return; } } if ($.fn.popup === undefined) { module.error(error.popup); return; } if (!$container.length) { //prepend the popup element to the activator's parent so that it has less chance of messing with //the styling (eg input action button needs to be the last child to have correct border radius) var $activatorParent = $activator.parent(), domPositionFunction = $activatorParent.closest(selector.append).length !== 0 ? 'appendTo' : 'prependTo'; $container = $('
').addClass(className.popup)[domPositionFunction]($activatorParent); } $container.addClass(className.calendar); if(isInverted){ $container.addClass(className.inverted); } var onVisible = function () { module.refreshTooltips(); return settings.onVisible.apply($container, arguments); }; var onHidden = function () { module.blur(); return settings.onHidden.apply($container, arguments) } if (!$input.length) { //no input, $container has to handle focus/blur $container.attr('tabindex', '0'); onVisible = function () { module.refreshTooltips(); module.focus(); return settings.onVisible.apply($container, arguments); }; } var onShow = function () { //reset the focus date onShow module.set.focusDate(module.get.date()); module.set.mode(module.get.validatedMode(settings.startMode)); return settings.onShow.apply($container, arguments); }; var on = module.setting('on'); var options = $.extend({}, settings.popupOptions, { popup: $container, on: on, hoverable: on === 'hover', closable: on === 'click', onShow: onShow, onVisible: onVisible, onHide: settings.onHide, onHidden: onHidden }); module.popup(options); }, inline: function () { if ($activator.length && !settings.inline) { return; } settings.inline = true; $container = $('
').addClass(className.calendar).appendTo($module); if (!$input.length) { $container.attr('tabindex', '0'); } }, input: function () { if (settings.touchReadonly && $input.length && isTouch) { $input.prop('readonly', true); } module.check.disabled(); }, date: function () { var date; if (settings.initialDate) { date = parser.date(settings.initialDate, settings); } else if ($module.data(metadata.date) !== undefined) { date = parser.date($module.data(metadata.date), settings); } else if ($input.length) { date = parser.date($input.val(), settings); } module.set.date(date, settings.formatInput, false); module.set.mode(module.get.mode(), false); } }, trigger: { change: function() { var inputElement = $input[0] ; if(inputElement) { var events = document.createEvent('HTMLEvents'); module.verbose('Triggering native change event'); events.initEvent('change', true, false); inputElement.dispatchEvent(events); } } }, create: { calendar: function () { var i, r, c, p, row, cell, pageGrid; var mode = module.get.mode(), today = new Date(), date = module.get.date(), focusDate = module.get.focusDate(), display = module.helper.dateInRange(focusDate || date || settings.initialDate || today) ; if (!focusDate) { focusDate = display; module.set.focusDate(focusDate, false, false); } var isYear = mode === 'year', isMonth = mode === 'month', isDay = mode === 'day', isHour = mode === 'hour', isMinute = mode === 'minute', isTimeOnly = settings.type === 'time' ; var multiMonth = Math.max(settings.multiMonth, 1); var monthOffset = !isDay ? 0 : module.get.monthOffset(); var minute = display.getMinutes(), hour = display.getHours(), day = display.getDate(), startMonth = display.getMonth() + monthOffset, year = display.getFullYear() ; var columns = isDay ? settings.showWeekNumbers ? 8 : 7 : isHour ? 4 : timeGap['column']; var rows = isDay || isHour ? 6 : timeGap['row']; var pages = isDay ? multiMonth : 1; var container = $container; var tooltipPosition = container.hasClass("left") ? "right center" : "left center"; container.empty(); if (pages > 1) { pageGrid = $('
').addClass(className.grid).appendTo(container); } for (p = 0; p < pages; p++) { if (pages > 1) { var pageColumn = $('
').addClass(className.column).appendTo(pageGrid); container = pageColumn; } var month = startMonth + p; var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7; if (!settings.constantHeight && isDay) { var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn; rows = Math.ceil(requiredCells / 7); } var yearChange = isYear ? 10 : isMonth ? 1 : 0, monthChange = isDay ? 1 : 0, dayChange = isHour || isMinute ? 1 : 0, prevNextDay = isHour || isMinute ? day : 1, prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour), nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour), prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) : isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1), nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) : isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1) ; var tempMode = mode; if (isDay && settings.showWeekNumbers){ tempMode += ' andweek'; } var table = $('').addClass(className.table).addClass(tempMode).addClass(numberText[columns] + ' column').appendTo(container); if(isInverted){ table.addClass(className.inverted); } var textColumns = columns; //no header for time-only mode if (!isTimeOnly) { var thead = $('').appendTo(table); row = $('').appendTo(thead); cell = $('').appendTo(thead); if(settings.showWeekNumbers) { cell = $('').appendTo(table); i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0; for (r = 0; r < rows; r++) { row = $('').appendTo(tbody); if(isDay && settings.showWeekNumbers){ cell = $('').appendTo(tbody); var todayButton = $('
').attr('colspan', '' + columns).appendTo(row); var headerDate = isYear || isMonth ? new Date(year, 0, 1) : isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute); var headerText = $('').addClass(className.link).appendTo(cell); headerText.text(module.helper.dateFormat(formatter[mode+'Header'], headerDate)); var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') : isDay ? (settings.disableMonth ? 'year' : 'month') : 'day'; headerText.data(metadata.mode, newMode); if (p === 0) { var prev = $('').addClass(className.prev).appendTo(cell); prev.data(metadata.focusDate, prevDate); prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode)); $('').addClass(className.prevIcon).appendTo(prev); } if (p === pages - 1) { var next = $('').addClass(className.next).appendTo(cell); next.data(metadata.focusDate, nextDate); next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode)); $('').addClass(className.nextIcon).appendTo(next); } if (isDay) { row = $('
').appendTo(row); cell.text(settings.text.weekNo); cell.addClass(className.weekCell); textColumns--; } for (i = 0; i < textColumns; i++) { cell = $('').appendTo(row); cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings)); } } } var tbody = $('
').appendTo(row); cell.text(module.get.weekOfYear(year,month,i+1-settings.firstDayOfWeek)); cell.addClass(className.weekCell); } for (c = 0; c < textColumns; c++, i++) { var cellDate = isYear ? new Date(i, month, 1, hour, minute) : isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) : isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap); var cellText = isYear ? i : isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() : module.helper.dateFormat(formatter.cellTime,cellDate); cell = $('').addClass(className.cell).appendTo(row); cell.text(cellText); cell.data(metadata.date, cellDate); var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12); var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode); var eventDate; if (disabled) { var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates); if (disabledDate !== null && disabledDate[metadata.message]) { cell.attr("data-tooltip", disabledDate[metadata.message]); cell.attr("data-position", disabledDate[metadata.position] || tooltipPosition); if(disabledDate[metadata.inverted] || (isInverted && disabledDate[metadata.inverted] === undefined)) { cell.attr("data-inverted", ''); } if(disabledDate[metadata.variation]) { cell.attr("data-variation", disabledDate[metadata.variation]); } } if (mode === 'hour') { var disabledHour = module.helper.findHourAsObject(cellDate, mode, settings.disabledHours); if (disabledHour !== null && disabledHour[metadata.message]) { cell.attr("data-tooltip", disabledHour[metadata.message]); cell.attr("data-position", disabledHour[metadata.position] || tooltipPosition); if(disabledHour[metadata.inverted] || (isInverted && disabledHour[metadata.inverted] === undefined)) { cell.attr("data-inverted", ''); } if(disabledHour[metadata.variation]) { cell.attr("data-variation", disabledHour[metadata.variation]); } } } } else { eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates); if (eventDate !== null) { cell.addClass(eventDate[metadata.class] || settings.eventClass); if (eventDate[metadata.message]) { cell.attr("data-tooltip", eventDate[metadata.message]); cell.attr("data-position", eventDate[metadata.position] || tooltipPosition); if(eventDate[metadata.inverted] || (isInverted && eventDate[metadata.inverted] === undefined)) { cell.attr("data-inverted", ''); } if(eventDate[metadata.variation]) { cell.attr("data-variation", eventDate[metadata.variation]); } } } } var active = module.helper.dateEqual(cellDate, date, mode); var isToday = module.helper.dateEqual(cellDate, today, mode); cell.toggleClass(className.adjacentCell, adjacent && !eventDate); cell.toggleClass(className.disabledCell, disabled); cell.toggleClass(className.activeCell, active && !(adjacent && disabled)); if (!isHour && !isMinute) { cell.toggleClass(className.todayCell, !adjacent && isToday); } // Allow for external modifications of each cell var cellOptions = { mode: mode, adjacent: adjacent, disabled: disabled, active: active, today: isToday }; formatter.cell(cell, cellDate, cellOptions); if (module.helper.dateEqual(cellDate, focusDate, mode)) { //ensure that the focus date is exactly equal to the cell date //so that, if selected, the correct value is set module.set.focusDate(cellDate, false, false); } } } if (settings.today) { var todayRow = $('
').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow); todayButton.text(formatter.today(settings)); todayButton.data(metadata.date, today); } module.update.focus(false, table); if(settings.inline){ module.refreshTooltips(); } } } }, update: { focus: function (updateRange, container) { container = container || $container; var mode = module.get.mode(); var date = module.get.date(); var focusDate = module.get.focusDate(); var startDate = module.get.startDate(); var endDate = module.get.endDate(); var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null); container.find('td').each(function () { var cell = $(this); var cellDate = cell.data(metadata.date); if (!cellDate) { return; } var disabled = cell.hasClass(className.disabledCell); var active = cell.hasClass(className.activeCell); var adjacent = cell.hasClass(className.adjacentCell); var focused = module.helper.dateEqual(cellDate, focusDate, mode); var inRange = !rangeDate ? false : ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) || (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate))); cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled); if (module.helper.isTodayButton(cell)) { return; } cell.toggleClass(className.rangeCell, inRange && !active && !disabled); }); } }, refresh: function () { module.create.calendar(); }, refreshTooltips: function() { var winWidth = $(window).width(); $container.find('td[data-position]').each(function () { var cell = $(this); var tooltipWidth = window.getComputedStyle(cell[0], '::after').width.replace(/[^0-9\.]/g,''); var tooltipPosition = cell.attr('data-position'); // use a fallback width of 250 (calendar width) for IE/Edge (which return "auto") var calcPosition = (winWidth - cell.width() - (parseInt(tooltipWidth,10) || 250)) > cell.offset().left ? 'right' : 'left'; if(tooltipPosition.indexOf(calcPosition) === -1) { cell.attr('data-position',tooltipPosition.replace(/(left|right)/,calcPosition)); } }); }, bind: { events: function () { module.debug('Binding events'); $container.on('mousedown' + eventNamespace, module.event.mousedown); $container.on('touchstart' + eventNamespace, module.event.mousedown); $container.on('mouseup' + eventNamespace, module.event.mouseup); $container.on('touchend' + eventNamespace, module.event.mouseup); $container.on('mouseover' + eventNamespace, module.event.mouseover); if ($input.length) { $input.on('input' + eventNamespace, module.event.inputChange); $input.on('focus' + eventNamespace, module.event.inputFocus); $input.on('blur' + eventNamespace, module.event.inputBlur); $input.on('keydown' + eventNamespace, module.event.keydown); } else { $container.on('keydown' + eventNamespace, module.event.keydown); } } }, unbind: { events: function () { module.debug('Unbinding events'); $container.off(eventNamespace); if ($input.length) { $input.off(eventNamespace); } } }, event: { mouseover: function (event) { var target = $(event.target); var date = target.data(metadata.date); var mousedown = event.buttons === 1; if (date) { module.set.focusDate(date, false, true, mousedown); } }, mousedown: function (event) { if ($input.length) { //prevent the mousedown on the calendar causing the input to lose focus event.preventDefault(); } isTouchDown = event.type.indexOf('touch') >= 0; var target = $(event.target); var date = target.data(metadata.date); if (date) { module.set.focusDate(date, false, true, true); } }, mouseup: function (event) { //ensure input has focus so that it receives keydown events for calendar navigation module.focus(); event.preventDefault(); event.stopPropagation(); isTouchDown = false; var target = $(event.target); if (target.hasClass("disabled")) { return; } var parent = target.parent(); if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) { //clicked on a child element, switch to parent (used when clicking directly on prev/next icon element) target = parent; } var date = target.data(metadata.date); var focusDate = target.data(metadata.focusDate); var mode = target.data(metadata.mode); if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) { var forceSet = target.hasClass(className.today); module.selectDate(date, forceSet); } else if (focusDate) { module.set.focusDate(focusDate); } else if (mode) { module.set.mode(mode); } }, keydown: function (event) { var keyCode = event.which; if (keyCode === 9) { //tab module.popup('hide'); } if (module.popup('is visible')) { var mode = module.get.mode(); if (keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40) { //arrow keys var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : mode === 'minute' ? timeGap['column'] : 3; var increment = keyCode === 37 ? -1 : keyCode === 38 ? -bigIncrement : keyCode == 39 ? 1 : bigIncrement; increment *= mode === 'minute' ? settings.minTimeGap : 1; var focusDate = module.get.focusDate() || module.get.date() || new Date(); var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0); var month = focusDate.getMonth() + (mode === 'month' ? increment : 0); var day = focusDate.getDate() + (mode === 'day' ? increment : 0); var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0); var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0); var newFocusDate = new Date(year, month, day, hour, minute); if (settings.type === 'time') { newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate); } if (module.helper.isDateInRange(newFocusDate, mode)) { module.set.focusDate(newFocusDate); } } else if (keyCode === 13) { //enter var date = module.get.focusDate(); if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) { if (settings.onSelect.call(element, date, module.get.mode()) !== false) { module.selectDate(date); } } //disable form submission: event.preventDefault(); event.stopPropagation(); } else if (keyCode === 27) { module.popup('hide'); event.stopPropagation(); } } if (keyCode === 38 || keyCode === 40) { //arrow-up || arrow-down event.preventDefault(); //don't scroll module.popup('show'); } }, inputChange: function () { var val = $input.val(); var date = parser.date(val, settings); module.set.date(date, false); }, inputFocus: function () { $container.addClass(className.active); }, inputBlur: function () { $container.removeClass(className.active); if (settings.formatInput) { var date = module.get.date(); var text = module.helper.dateFormat(formatter[settings.type], date); $input.val(text); } if(selectionComplete){ module.trigger.change(); selectionComplete = false; } }, class: { mutation: function(mutations) { mutations.forEach(function(mutation) { if(mutation.attributeName === "class") { module.check.disabled(); } }); } } }, observeChanges: function() { if('MutationObserver' in window) { classObserver = new MutationObserver(module.event.class.mutation); module.debug('Setting up mutation observer', classObserver); module.observe.class(); } }, disconnect: { classObserver: function() { if($input.length && classObserver) { classObserver.disconnect(); } } }, observe: { class: function() { if($input.length && classObserver) { classObserver.observe($module[0], { attributes : true }); } } }, is: { disabled: function() { return $module.hasClass(className.disabled); } }, check: { disabled: function(){ $input.attr('tabindex',module.is.disabled() ? -1 : 0); } }, get: { weekOfYear: function(weekYear,weekMonth,weekDay) { // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm var ms1d = 864e5, // milliseconds in a day ms7d = 7 * ms1d; // milliseconds in a week return function() { // return a closure so constants get calculated only once var DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an Absolute Day Number AWN = Math.floor(DC3 / 7), // an Absolute Week Number Wyr = new Date(AWN * ms7d).getUTCFullYear(); return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1; }(); }, formattedDate: function(format, date) { return module.helper.dateFormat(format || formatter[settings.type], date || module.get.date()); }, date: function () { return module.helper.sanitiseDate($module.data(metadata.date)) || null; }, inputDate: function() { return $input.val(); }, focusDate: function () { return $module.data(metadata.focusDate) || null; }, startDate: function () { var startModule = module.get.calendarModule(settings.startCalendar); return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null; }, endDate: function () { var endModule = module.get.calendarModule(settings.endCalendar); return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null; }, minDate: function() { return $module.data(metadata.minDate) || null; }, maxDate: function() { return $module.data(metadata.maxDate) || null; }, monthOffset: function () { return $module.data(metadata.monthOffset) || settings.monthOffset || 0; }, mode: function () { //only returns valid modes for the current settings var mode = $module.data(metadata.mode) || settings.startMode; return module.get.validatedMode(mode); }, validatedMode: function(mode){ var validModes = module.get.validModes(); if ($.inArray(mode, validModes) >= 0) { return mode; } return settings.type === 'time' ? 'hour' : settings.type === 'month' ? 'month' : settings.type === 'year' ? 'year' : 'day'; }, type: function() { return $module.data(metadata.type) || settings.type; }, validModes: function () { var validModes = []; if (settings.type !== 'time') { if (!settings.disableYear || settings.type === 'year') { validModes.push('year'); } if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') { validModes.push('month'); } if (settings.type.indexOf('date') >= 0) { validModes.push('day'); } } if (settings.type.indexOf('time') >= 0) { validModes.push('hour'); if (!settings.disableMinute) { validModes.push('minute'); } } return validModes; }, isTouch: function () { try { document.createEvent('TouchEvent'); return true; } catch (e) { return false; } }, calendarModule: function (selector) { if (!selector) { return null; } if (!(selector instanceof $)) { selector = $(document).find(selector).first(); } //assume range related calendars are using the same namespace return selector.data(moduleNamespace); } }, set: { date: function (date, updateInput, fireChange) { updateInput = updateInput !== false; fireChange = fireChange !== false; date = module.helper.sanitiseDate(date); date = module.helper.dateInRange(date); var mode = module.get.mode(); var text = module.helper.dateFormat(formatter[settings.type],date); if (fireChange && settings.onBeforeChange.call(element, date, text, mode) === false) { return false; } module.set.focusDate(date); if (settings.isDisabled(date, mode)) { return false; } var endDate = module.get.endDate(); if (!!endDate && !!date && date > endDate) { //selected date is greater than end date in range, so clear end date module.set.endDate(undefined); } module.set.dataKeyValue(metadata.date, date); if (updateInput && $input.length) { $input.val(text); } if (fireChange) { settings.onChange.call(element, date, text, mode); } }, startDate: function (date, refreshCalendar) { date = module.helper.sanitiseDate(date); var startModule = module.get.calendarModule(settings.startCalendar); if (startModule) { startModule.set.date(date); } module.set.dataKeyValue(metadata.startDate, date, refreshCalendar); }, endDate: function (date, refreshCalendar) { date = module.helper.sanitiseDate(date); var endModule = module.get.calendarModule(settings.endCalendar); if (endModule) { endModule.set.date(date); } module.set.dataKeyValue(metadata.endDate, date, refreshCalendar); }, focusDate: function (date, refreshCalendar, updateFocus, updateRange) { date = module.helper.sanitiseDate(date); date = module.helper.dateInRange(date); var isDay = module.get.mode() === 'day'; var oldFocusDate = module.get.focusDate(); if (isDay && date && oldFocusDate) { var yearDelta = date.getFullYear() - oldFocusDate.getFullYear(); var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth(); if (monthDelta) { var monthOffset = module.get.monthOffset() - monthDelta; module.set.monthOffset(monthOffset, false); } } var changed = module.set.dataKeyValue(metadata.focusDate, date, !!date && refreshCalendar); updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange; focusDateUsedForRange = updateRange; if (updateFocus) { module.update.focus(updateRange); } }, minDate: function (date) { date = module.helper.sanitiseDate(date); if (settings.maxDate !== null && settings.maxDate <= date) { module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate); } else { module.setting('minDate', date); module.set.dataKeyValue(metadata.minDate, date); } }, maxDate: function (date) { date = module.helper.sanitiseDate(date); if (settings.minDate !== null && settings.minDate >= date) { module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate); } else { module.setting('maxDate', date); module.set.dataKeyValue(metadata.maxDate, date); } }, monthOffset: function (monthOffset, refreshCalendar) { var multiMonth = Math.max(settings.multiMonth, 1); monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset)); module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar); }, mode: function (mode, refreshCalendar) { module.set.dataKeyValue(metadata.mode, mode, refreshCalendar); }, dataKeyValue: function (key, value, refreshCalendar) { var oldValue = $module.data(key); var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects if (value) { $module.data(key, value); } else { $module.removeData(key); } refreshCalendar = refreshCalendar !== false && !equal; if (refreshCalendar) { module.refresh(); } return !equal; } }, selectDate: function (date, forceSet) { module.verbose('New date selection', date); var mode = module.get.mode(); var complete = forceSet || mode === 'minute' || (settings.disableMinute && mode === 'hour') || (settings.type === 'date' && mode === 'day') || (settings.type === 'month' && mode === 'month') || (settings.type === 'year' && mode === 'year'); if (complete) { var canceled = module.set.date(date) === false; if (!canceled) { selectionComplete = true; if(settings.closable) { module.popup('hide'); //if this is a range calendar, focus the container or input. This will open the popup from its event listeners. var endModule = module.get.calendarModule(settings.endCalendar); if (endModule) { endModule.refresh(); if (endModule.setting('on') !== 'focus') { endModule.popup('show'); } endModule.focus(); } } } } else { var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') : mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute'; module.set.mode(newMode); if (mode === 'hour' || (mode === 'day' && module.get.date())) { //the user has chosen enough to consider a valid date/time has been chosen module.set.date(date, true, false); } else { module.set.focusDate(date); } } }, changeDate: function (date) { module.set.date(date); }, clear: function () { module.set.date(undefined); }, popup: function () { return $activator.popup.apply($activator, arguments); }, focus: function () { if ($input.length) { $input.focus(); } else { $container.focus(); } }, blur: function () { if ($input.length) { $input.blur(); } else { $container.blur(); } }, helper: { dateFormat: function(format,date) { if (!(date instanceof Date)) { return ''; } if(typeof format === 'function') { return format.call(module, date, settings); } var D = date.getDate(), M = date.getMonth(), Y = date.getFullYear(), d = date.getDay(), H = date.getHours(), m = date.getMinutes(), s = date.getSeconds(), w = module.get.weekOfYear(Y,M,D+1-settings.firstDayOfWeek), h = H % 12 || 12, a = H < 12 ? settings.text.am.toLowerCase() : settings.text.pm.toLowerCase(), tokens = { D: D, DD: ('0'+D).slice(-2), M: M + 1, MM: ('0'+(M+1)).slice(-2), MMM: settings.text.monthsShort[M], MMMM: settings.text.months[M], Y: Y, YY: String(Y).slice(2), YYYY: Y, d: d, dd: settings.text.dayNamesShort[d].slice(0,2), ddd: settings.text.dayNamesShort[d], dddd: settings.text.dayNames[d], h: h, hh: ('0'+h).slice(-2), H: H, HH: ('0'+H).slice(-2), m: m, mm: ('0'+m).slice(-2), s: s, ss: ('0'+s).slice(-2), a: a, A: a.toUpperCase(), S: ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D % 100 - D % 10 !== 10) * D % 10], w: w, ww: ('0'+w).slice(-2) } ; return format.replace(settings.regExp.token, function (match) { if (match in tokens) { return tokens[match]; } return match.slice(1, match.length - 1); }); }, isDisabled: function(date, mode) { return (mode === 'day' || mode === 'month' || mode === 'year' || mode === 'hour') && (((mode === 'day' && settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function(d){ if(typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { return module.helper.dateEqual(date, d, mode); } if (d !== null && typeof d === 'object') { if (d[metadata.year]) { if (typeof d[metadata.year] === 'number') { return date.getFullYear() == d[metadata.year]; } else if (Array.isArray(d[metadata.year])) { return d[metadata.year].indexOf(date.getFullYear()) > -1; } } else if (d[metadata.month]) { if (typeof d[metadata.month] === 'number') { return date.getMonth() == d[metadata.month]; } else if (Array.isArray(d[metadata.month])) { return d[metadata.month].indexOf(date.getMonth()) > -1; } else if (d[metadata.month] instanceof Date) { var sdate = module.helper.sanitiseDate(d[metadata.month]); return (date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear()) } } else if (d[metadata.date] && mode === 'day') { if (d[metadata.date] instanceof Date) { return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode); } else if (Array.isArray(d[metadata.date])) { return d[metadata.date].some(function(idate) { return module.helper.dateEqual(date, idate, mode); }); } } } })) || (mode === 'hour' && settings.disabledHours.some(function(d){ if (typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { return module.helper.dateEqual(date, d, mode); } else if (typeof d === 'number') { return date.getHours() === d; } if (d !== null && typeof d === 'object') { var blocked = true; if (d[metadata.date]) { if (d[metadata.date] instanceof Date) { blocked = module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date])); } else if (Array.isArray(d[metadata.date])) { return d[metadata.date].some(function(idate) { blocked = module.helper.dateEqual(date, idate, mode); }); } } if (d[metadata.days]) { if (typeof d[metadata.days] === 'number') { blocked = date.getDay() == d[metadata.days]; } else if (Array.isArray(d[metadata.days])) { blocked = d[metadata.days].indexOf(date.getDay()) > -1; } } if (d[metadata.hours]) { if (typeof d[metadata.hours] === 'number') { return blocked && date.getHours() == d[metadata.hours]; } else if (Array.isArray(d[metadata.hours])) { return blocked && d[metadata.hours].indexOf(date.getHours()) > -1; } } } }))); }, isEnabled: function(date, mode) { if (mode === 'day') { return settings.enabledDates.length === 0 || settings.enabledDates.some(function(d){ if(typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date) { return module.helper.dateEqual(date, d, mode); } if (d !== null && typeof d === 'object' && d[metadata.date]) { return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode); } }); } else { return true; } }, findDayAsObject: function(date, mode, dates) { if (mode === 'day' || mode === 'month' || mode === 'year') { var d; for (var i = 0; i < dates.length; i++) { d = dates[i]; if(typeof d === 'string') { d = module.helper.sanitiseDate(d); } if (d instanceof Date && module.helper.dateEqual(date, d, mode)) { var dateObject = {}; dateObject[metadata.date] = d; return dateObject; } else if (d !== null && typeof d === 'object') { if (d[metadata.year]) { if (typeof d[metadata.year] === 'number' && date.getFullYear() == d[metadata.year]) { return d; } else if (Array.isArray(d[metadata.year])) { if (d[metadata.year].indexOf(date.getFullYear()) > -1) { return d; } } } else if (d[metadata.month]) { if (typeof d[metadata.month] === 'number' && date.getMonth() == d[metadata.month]) { return d; } else if (Array.isArray(d[metadata.month])) { if (d[metadata.month].indexOf(date.getMonth()) > -1) { return d; } } else if (d[metadata.month] instanceof Date) { var sdate = module.helper.sanitiseDate(d[metadata.month]); if ((date.getMonth() == sdate.getMonth()) && (date.getFullYear() == sdate.getFullYear())) { return d; } } } else if (d[metadata.date] && mode === 'day') { if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode)) { return d; } else if (Array.isArray(d[metadata.date])) { if(d[metadata.date].some(function(idate) { return module.helper.dateEqual(date, idate, mode); })) { return d; } } } } } } return null; }, findHourAsObject: function(date, mode, hours) { if (mode === 'hour') { var d; var hourCheck = function(date, d) { if (d[metadata.hours]) { if (typeof d[metadata.hours] === 'number' && date.getHours() == d[metadata.hours]) { return d; } else if (Array.isArray(d[metadata.hours])) { if (d[metadata.hours].indexOf(date.getHours()) > -1) { return d; } } } } for (var i = 0; i < hours.length; i++) { d = hours[i]; if (typeof d === 'number' && date.getHours() == d) { return null; } else if (d !== null && typeof d === 'object') { if (d[metadata.days] && hourCheck(date,d)) { if (typeof d[metadata.days] === 'number' && date.getDay() == d[metadata.days]) { return d; } else if (Array.isArray(d[metadata.days])) { if (d[metadata.days].indexOf(date.getDay()) > -1) { return d; } } } else if (d[metadata.date] && hourCheck(date,d)) { if (d[metadata.date] instanceof Date && module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]))) { return d; } else if (Array.isArray(d[metadata.date])) { if (d[metadata.date].some(function(idate) { return module.helper.dateEqual(date, idate, mode); })) { return d; } } } else if (hourCheck(date,d)) { return d; } } } } return null; }, sanitiseDate: function (date) { if (!(date instanceof Date)) { date = parser.date('' + date, settings); } if (!date || isNaN(date.getTime())) { return null; } return date; }, dateDiff: function (date1, date2, mode) { mode = mode || 'day'; var isTimeOnly = settings.type === 'time'; var isYear = mode === 'year'; var isYearOrMonth = isYear || mode === 'month'; var isMinute = mode === 'minute'; var isHourOrMinute = isMinute || mode === 'hour'; //only care about a minute accuracy of settings.minTimeGap date1 = new Date( isTimeOnly ? 2000 : date1.getFullYear(), isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(), isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(), !isHourOrMinute ? 0 : date1.getHours(), !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap)); date2 = new Date( isTimeOnly ? 2000 : date2.getFullYear(), isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(), isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(), !isHourOrMinute ? 0 : date2.getHours(), !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap)); return date2.getTime() - date1.getTime(); }, dateEqual: function (date1, date2, mode) { return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0; }, isDateInRange: function (date, mode, minDate, maxDate) { if (!minDate && !maxDate) { var startDate = module.get.startDate(); minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; maxDate = settings.maxDate; } minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap)); return !(!date || (minDate && module.helper.dateDiff(date, minDate, mode) > 0) || (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0)); }, dateInRange: function (date, minDate, maxDate) { if (!minDate && !maxDate) { var startDate = module.get.startDate(); minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate; maxDate = settings.maxDate; } minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap)); var isTimeOnly = settings.type === 'time'; return !date ? date : (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ? (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) : (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ? (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) : date; }, mergeDateTime: function (date, time) { return (!date || !time) ? time : new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes()); }, isTodayButton: function(element) { return element.text() === settings.text.today; } }, setting: function (name, value) { module.debug('Changing setting', name, value); if ($.isPlainObject(name)) { $.extend(true, settings, name); } else if (value !== undefined) { if ($.isPlainObject(settings[name])) { $.extend(true, settings[name], value); } else { settings[name] = value; } } else { return settings[name]; } }, internal: function (name, value) { if( $.isPlainObject(name) ) { $.extend(true, module, name); } else if(value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function () { if (!settings.silent && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function () { if (!settings.silent && settings.verbose && settings.debug) { if (settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function () { if (!settings.silent) { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); } }, performance: { log: function (message) { var currentTime, executionTime, previousTime ; if (settings.performance) { currentTime = new Date().getTime(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ 'Name': message[0], 'Arguments': [].slice.call(message, 1) || '', 'Element': element, 'Execution Time': executionTime }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(module.performance.display, 500); }, display: function () { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function (index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if (moduleSelector) { title += ' \'' + moduleSelector + '\''; } if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) { console.groupCollapsed(title); if (console.table) { console.table(performance); } else { $.each(performance, function (index, data) { console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms'); }); } console.groupEnd(); } performance = []; } }, invoke: function (query, passedArguments, context) { var object = instance, maxDepth, found, response ; passedArguments = passedArguments || queryArguments; context = context || element; if (typeof query == 'string' && object !== undefined) { query = query.split(/[\. ]/); maxDepth = query.length - 1; $.each(query, function (depth, value) { var camelCaseValue = (depth != maxDepth) ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) : query ; if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) { object = object[camelCaseValue]; } else if (object[camelCaseValue] !== undefined) { found = object[camelCaseValue]; return false; } else if ($.isPlainObject(object[value]) && (depth != maxDepth)) { object = object[value]; } else if (object[value] !== undefined) { found = object[value]; return false; } else { module.error(error.method, query); return false; } }); } if ($.isFunction(found)) { response = found.apply(context, passedArguments); } else if (found !== undefined) { response = found; } if (Array.isArray(returnedValue)) { returnedValue.push(response); } else if (returnedValue !== undefined) { returnedValue = [returnedValue, response]; } else if (response !== undefined) { returnedValue = response; } return found; } }; if (methodInvoked) { if (instance === undefined) { module.initialize(); } module.invoke(query); } else { if (instance !== undefined) { instance.invoke('destroy'); } module.initialize(); } }) ; return (returnedValue !== undefined) ? returnedValue : this ; }; $.fn.calendar.settings = { name : 'Calendar', namespace : 'calendar', silent: false, debug: false, verbose: false, performance: true, type : 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year' firstDayOfWeek : 0, // day for first day column (0 = Sunday) constantHeight : true, // add rows to shorter months to keep day calendar height consistent (6 rows) today : false, // show a 'today/now' button at the bottom of the calendar closable : true, // close the popup after selecting a date/time monthFirst : true, // month before day when parsing date from text touchReadonly : true, // set input to readonly on touch devices inline : false, // create the calendar inline instead of inside a popup on : null, // when to show the popup (defaults to 'focus' for input, 'click' for others) initialDate : null, // date to display initially when no date is selected (null = now) startMode : false, // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day') minDate : null, // minimum date/time that can be selected, dates/times before are disabled maxDate : null, // maximum date/time that can be selected, dates/times after are disabled disableYear : false, // disable year selection mode disableMonth : false, // disable month selection mode disableMinute : false, // disable minute selection mode formatInput : true, // format the input text upon input blur and module creation startCalendar : null, // jquery object or selector for another calendar that represents the start date of a date range endCalendar : null, // jquery object or selector for another calendar that represents the end date of a date range multiMonth : 1, // show multiple months when in 'day' mode monthOffset : 0, // position current month by offset when multimonth > 1 minTimeGap : 5, showWeekNumbers : false, // show Number of Week at the very first column of a dayView disabledHours : [], // specific hour(s) which won't be selectable and contain additional information. disabledDates : [], // specific day(s) which won't be selectable and contain additional information. disabledDaysOfWeek : [], // day(s) which won't be selectable(s) (0 = Sunday) enabledDates : [], // specific day(s) which will be selectable, all other days will be disabled eventDates : [], // specific day(s) which will be shown in a different color and using tooltips centuryBreak : 60, // starting short year until 99 where it will be assumed to belong to the last century currentCentury : 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1) selectAdjacentDays : false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable. // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden) popupOptions: { position: 'bottom left', lastResort: 'bottom left', prefer: 'opposite', observeChanges: false, hideOnScroll: false }, text: { days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], today: 'Today', now: 'Now', am: 'AM', pm: 'PM', weekNo: 'Week' }, formatter: { yearHeader: function (date, settings) { var decadeYear = Math.ceil(date.getFullYear() / 10) * 10; return (decadeYear - 9) + ' - ' + (decadeYear + 2); }, monthHeader: 'YYYY', dayHeader: 'MMMM YYYY', hourHeader: 'MMMM D, YYYY', minuteHeader: 'MMMM D, YYYY', dayColumnHeader: function (day, settings) { return settings.text.days[day]; }, datetime: 'MMMM D, YYYY h:mm A', date: 'MMMM D, YYYY', time: 'h:mm A', cellTime: 'h:mm A', month: 'MMMM YYYY', year: 'YYYY', today: function (settings) { return settings.type === 'date' ? settings.text.today : settings.text.now; }, cell: function (cell, date, cellOptions) { } }, parser: { date: function (text, settings) { if (text instanceof Date) { return text; } if (!text) { return null; } text = String(text).trim().replace(/([.:\/\-])\s+/g,'$1').replace(/\s+([.:\/-])/g,'$1').replace(/\s+/g,' '); if (text.length === 0) { return null; } if(text.match(/^[0-9]{4}[\/\-\.][0-9]{1,2}[\/\-\.][0-9]{1,2}$/)){ text = text.replace(/[\/\-\.]/g,'/') + ' 00:00:00'; } // Reverse date and month in some cases text = settings.monthFirst || !text.match(/^[0-9]{1,2}[\/\-\.]/) ? text : text.replace(/[\/\-\.]/g,'/').replace(/([0-9]+)\/([0-9]+)/,'$2/$1'); var textDate = new Date(text); var numberOnly = text.match(/^[0-9]+$/) !== null; if(!numberOnly && !isNaN(textDate.getDate())) { return textDate; } text = text.toLowerCase(); var i, j, k; var minute = -1, hour = -1, day = -1, month = -1, year = -1; var isAm = undefined; var isTimeOnly = settings.type === 'time'; var isDateOnly = settings.type.indexOf('time') < 0; var words = text.split(settings.regExp.dateWords), word; var numbers = text.split(settings.regExp.dateNumbers), number; var parts; var monthString; if (!isDateOnly) { //am/pm isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true : $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined; //time with ':' for (i = 0; i < numbers.length; i++) { number = numbers[i]; if (number.indexOf(':') >= 0) { if (hour < 0 || minute < 0) { parts = number.split(':'); for (k = 0; k < Math.min(2, parts.length); k++) { j = parseInt(parts[k]); if (isNaN(j)) { j = 0; } if (k === 0) { hour = j % 24; } else { minute = j % 60; } } } numbers.splice(i, 1); } } } if (!isTimeOnly) { //textual month for (i = 0; i < words.length; i++) { word = words[i]; if (word.length <= 0) { continue; } for (j = 0; j < settings.text.months.length; j++) { monthString = settings.text.months[j]; monthString = monthString.substring(0, word.length).toLowerCase(); if (monthString === word) { month = j + 1; break; } } if (month >= 0) { break; } } //year > settings.centuryBreak for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i]); if (isNaN(j)) { continue; } if (j >= settings.centuryBreak && i === numbers.length-1) { if (j <= 99) { j += settings.currentCentury - 100; } year = j; numbers.splice(i, 1); break; } } //numeric month if (month < 0) { for (i = 0; i < numbers.length; i++) { k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1; j = parseInt(numbers[k]); if (isNaN(j)) { continue; } if (1 <= j && j <= 12) { month = j; numbers.splice(k, 1); break; } } } //day for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i]); if (isNaN(j)) { continue; } if (1 <= j && j <= 31) { day = j; numbers.splice(i, 1); break; } } //year <= settings.centuryBreak if (year < 0) { for (i = numbers.length - 1; i >= 0; i--) { j = parseInt(numbers[i]); if (isNaN(j)) { continue; } if (j <= 99) { j += settings.currentCentury; } year = j; numbers.splice(i, 1); break; } } } if (!isDateOnly) { //hour if (hour < 0) { for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i]); if (isNaN(j)) { continue; } if (0 <= j && j <= 23) { hour = j; numbers.splice(i, 1); break; } } } //minute if (minute < 0) { for (i = 0; i < numbers.length; i++) { j = parseInt(numbers[i]); if (isNaN(j)) { continue; } if (0 <= j && j <= 59) { minute = j; numbers.splice(i, 1); break; } } } } if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) { return null; } if (minute < 0) { minute = 0; } if (hour < 0) { hour = 0; } if (day < 0) { day = 1; } if (month < 0) { month = 1; } if (year < 0) { year = new Date().getFullYear(); } if (isAm !== undefined) { if (isAm) { if (hour === 12) { hour = 0; } } else if (hour < 12) { hour += 12; } } var date = new Date(year, month - 1, day, hour, minute); if (date.getMonth() !== month - 1 || date.getFullYear() !== year) { //month or year don't match up, switch to last day of the month date = new Date(year, month, 0, hour, minute); } return isNaN(date.getTime()) ? null : date; } }, // callback before date is changed, return false to cancel the change onBeforeChange: function (date, text, mode) { return true; }, // callback when date changes onChange: function (date, text, mode) { }, // callback before show animation, return false to prevent show onShow: function () { }, // callback after show animation onVisible: function () { }, // callback before hide animation, return false to prevent hide onHide: function () { }, // callback after hide animation onHidden: function () { }, // callback before item is selected, return false to prevent selection onSelect: function (date, mode) { }, // is the given date disabled? isDisabled: function (date, mode) { return false; }, selector: { popup: '.ui.popup', input: 'input', activator: 'input', append: '.inline.field,.inline.fields' }, regExp: { dateWords: /[^A-Za-z\u00C0-\u024F]+/g, dateNumbers: /[^\d:]+/g, token: /d{1,4}|D{1,2}|M{1,4}|YY(?:YY)?|([Hhmsw])\1?|[SAaY]|"[^"]*"|'[^']*'/g }, error: { popup: 'UI Popup, a required component is not included in this page', method: 'The method you called is not defined.' }, className: { calendar: 'calendar', active: 'active', popup: 'ui popup', grid: 'ui equal width grid', column: 'column', table: 'ui celled center aligned unstackable table', inverted: 'inverted', prev: 'prev link', next: 'next link', prevIcon: 'chevron left icon', nextIcon: 'chevron right icon', link: 'link', cell: 'link', disabledCell: 'disabled', weekCell: 'disabled', adjacentCell: 'adjacent', activeCell: 'active', rangeCell: 'range', focusCell: 'focus', todayCell: 'today', today: 'today link', disabled: 'disabled' }, metadata: { date: 'date', focusDate: 'focusDate', startDate: 'startDate', endDate: 'endDate', minDate: 'minDate', maxDate: 'maxDate', mode: 'mode', type: 'type', monthOffset: 'monthOffset', message: 'message', class: 'class', inverted: 'inverted', variation: 'variation', position: 'position', month: 'month', year: 'year', hours: 'hours', days: 'days' }, eventClass: 'blue' }; })(jQuery, window, document); /*! * # Fomantic-UI 2.9.0 - Checkbox * http://github.com/fomantic/Fomantic-UI/ * * * Released under the MIT license * http://opensource.org/licenses/MIT * */ ;(function ($, window, document, undefined) { 'use strict'; $.isFunction = $.isFunction || function(obj) { return typeof obj === "function" && typeof obj.nodeType !== "number"; }; window = (typeof window != 'undefined' && window.Math == Math) ? window : (typeof self != 'undefined' && self.Math == Math) ? self : Function('return this')() ; $.fn.checkbox = function(parameters) { var $allModules = $(this), moduleSelector = $allModules.selector || '', time = new Date().getTime(), performance = [], query = arguments[0], methodInvoked = (typeof query == 'string'), queryArguments = [].slice.call(arguments, 1), returnedValue ; $allModules .each(function() { var settings = $.extend(true, {}, $.fn.checkbox.settings, parameters), className = settings.className, namespace = settings.namespace, selector = settings.selector, error = settings.error, eventNamespace = '.' + namespace, moduleNamespace = 'module-' + namespace, $module = $(this), $label = $(this).children(selector.label), $input = $(this).children(selector.input), input = $input[0], initialLoad = false, shortcutPressed = false, instance = $module.data(moduleNamespace), observer, element = this, module ; module = { initialize: function() { module.verbose('Initializing checkbox', settings); module.create.label(); module.bind.events(); module.set.tabbable(); module.hide.input(); module.observeChanges(); module.instantiate(); module.setup(); }, instantiate: function() { module.verbose('Storing instance of module', module); instance = module; $module .data(moduleNamespace, module) ; }, destroy: function() { module.verbose('Destroying module'); module.unbind.events(); module.show.input(); $module.removeData(moduleNamespace); }, fix: { reference: function() { if( $module.is(selector.input) ) { module.debug('Behavior called on adjusting invoked element'); $module = $module.closest(selector.checkbox); module.refresh(); } } }, setup: function() { module.set.initialLoad(); if( module.is.indeterminate() ) { module.debug('Initial value is indeterminate'); module.indeterminate(); } else if( module.is.checked() ) { module.debug('Initial value is checked'); module.check(); } else { module.debug('Initial value is unchecked'); module.uncheck(); } module.remove.initialLoad(); }, refresh: function() { $label = $module.children(selector.label); $input = $module.children(selector.input); input = $input[0]; }, hide: { input: function() { module.verbose('Modifying z-index to be unselectable'); $input.addClass(className.hidden); } }, show: { input: function() { module.verbose('Modifying z-index to be selectable'); $input.removeClass(className.hidden); } }, observeChanges: function() { if('MutationObserver' in window) { observer = new MutationObserver(function(mutations) { module.debug('DOM tree modified, updating selector cache'); module.refresh(); }); observer.observe(element, { childList : true, subtree : true }); module.debug('Setting up mutation observer', observer); } }, attachEvents: function(selector, event) { var $element = $(selector) ; event = $.isFunction(module[event]) ? module[event] : module.toggle ; if($element.length > 0) { module.debug('Attaching checkbox events to element', selector, event); $element .on('click' + eventNamespace, event) ; } else { module.error(error.notFound); } }, preventDefaultOnInputTarget: function() { if(typeof event !== 'undefined' && event !== null && $(event.target).is(selector.input)) { module.verbose('Preventing default check action after manual check action'); event.preventDefault(); } }, event: { change: function(event) { if( !module.should.ignoreCallbacks() ) { settings.onChange.call(input); } }, click: function(event) { var $target = $(event.target) ; if( $target.is(selector.input) ) { module.verbose('Using default check action on initialized checkbox'); return; } if( $target.is(selector.link) ) { module.debug('Clicking link inside checkbox, skipping toggle'); return; } module.toggle(); $input.focus(); event.preventDefault(); }, keydown: function(event) { var key = event.which, keyCode = { enter : 13, space : 32, escape : 27, left : 37, up : 38, right : 39, down : 40 } ; var r = module.get.radios(), rIndex = r.index($module), rLen = r.length, checkIndex = false; if(key == keyCode.left || key == keyCode.up) { checkIndex = (rIndex === 0 ? rLen : rIndex) - 1; } else if(key == keyCode.right || key == keyCode.down) { checkIndex = rIndex === rLen-1 ? 0 : rIndex+1; } if (!module.should.ignoreCallbacks() && checkIndex !== false) { if(settings.beforeUnchecked.apply(input)===false) { module.verbose('Option not allowed to be unchecked, cancelling key navigation'); return false; } if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) { module.verbose('Next option should not allow check, cancelling key navigation'); return false; } } shortcutPressed = false; if(key == keyCode.escape) { module.verbose('Escape key pressed blurring field'); $input.blur(); shortcutPressed = true; event.stopPropagation(); } else if(!event.ctrlKey && module.can.change()) { if( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey) ) { module.verbose('Enter/space key pressed, toggling checkbox'); module.toggle(); shortcutPressed = true; } else if($module.is('.toggle, .slider') && !module.is.radio()) { if(key == keyCode.left && module.is.checked()) { module.uncheck(); shortcutPressed = true; } else if(key == keyCode.right && module.is.unchecked()) { module.check(); shortcutPressed = true; } } } }, keyup: function(event) { if(shortcutPressed) { event.preventDefault(); } } }, check: function() { if( !module.should.allowCheck() ) { return; } module.debug('Checking checkbox', $input); module.set.checked(); if( !module.should.ignoreCallbacks() ) { settings.onChecked.call(input); module.trigger.change(); } module.preventDefaultOnInputTarget(); }, uncheck: function() { if( !module.should.allowUncheck() ) { return; } module.debug('Unchecking checkbox'); module.set.unchecked(); if( !module.should.ignoreCallbacks() ) { settings.onUnchecked.call(input); module.trigger.change(); } module.preventDefaultOnInputTarget(); }, indeterminate: function() { if( module.should.allowIndeterminate() ) { module.debug('Checkbox is already indeterminate'); return; } module.debug('Making checkbox indeterminate'); module.set.indeterminate(); if( !module.should.ignoreCallbacks() ) { settings.onIndeterminate.call(input); module.trigger.change(); } }, determinate: function() { if( module.should.allowDeterminate() ) { module.debug('Checkbox is already determinate'); return; } module.debug('Making checkbox determinate'); module.set.determinate(); if( !module.should.ignoreCallbacks() ) { settings.onDeterminate.call(input); module.trigger.change(); } }, enable: function() { if( module.is.enabled() ) { module.debug('Checkbox is already enabled'); return; } module.debug('Enabling checkbox'); module.set.enabled(); if( !module.should.ignoreCallbacks() ) { settings.onEnable.call(input); // preserve legacy callbacks settings.onEnabled.call(input); } }, disable: function() { if( module.is.disabled() ) { module.debug('Checkbox is already disabled'); return; } module.debug('Disabling checkbox'); module.set.disabled(); if( !module.should.ignoreCallbacks() ) { settings.onDisable.call(input); // preserve legacy callbacks settings.onDisabled.call(input); } }, get: { radios: function() { var name = module.get.name() ; return $('input[name="' + name + '"]').closest(selector.checkbox); }, otherRadios: function() { return module.get.radios().not($module); }, name: function() { return $input.attr('name'); } }, is: { initialLoad: function() { return initialLoad; }, radio: function() { return ($input.hasClass(className.radio) || $input.attr('type') == 'radio'); }, indeterminate: function() { return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate'); }, checked: function() { return $input.prop('checked') !== undefined && $input.prop('checked'); }, disabled: function() { return $input.prop('disabled') !== undefined && $input.prop('disabled'); }, enabled: function() { return !module.is.disabled(); }, determinate: function() { return !module.is.indeterminate(); }, unchecked: function() { return !module.is.checked(); } }, should: { allowCheck: function() { if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) { module.debug('Should not allow check, checkbox is already checked'); return false; } if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) { module.debug('Should not allow check, beforeChecked cancelled'); return false; } return true; }, allowUncheck: function() { if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) { module.debug('Should not allow uncheck, checkbox is already unchecked'); return false; } if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) { module.debug('Should not allow uncheck, beforeUnchecked cancelled'); return false; } return true; }, allowIndeterminate: function() { if(module.is.indeterminate() && !module.is.initialLoad() ) { module.debug('Should not allow indeterminate, checkbox is already indeterminate'); return false; } if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) { module.debug('Should not allow indeterminate, beforeIndeterminate cancelled'); return false; } return true; }, allowDeterminate: function() { if(module.is.determinate() && !module.is.initialLoad() ) { module.debug('Should not allow determinate, checkbox is already determinate'); return false; } if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) { module.debug('Should not allow determinate, beforeDeterminate cancelled'); return false; } return true; }, ignoreCallbacks: function() { return (initialLoad && !settings.fireOnInit); } }, can: { change: function() { return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') ); }, uncheck: function() { return (typeof settings.uncheckable === 'boolean') ? settings.uncheckable : !module.is.radio() ; } }, set: { initialLoad: function() { initialLoad = true; }, checked: function() { module.verbose('Setting class to checked'); $module .removeClass(className.indeterminate) .addClass(className.checked) ; if( module.is.radio() ) { module.uncheckOthers(); } if(!module.is.indeterminate() && module.is.checked()) { module.debug('Input is already checked, skipping input property change'); return; } module.verbose('Setting state to checked', input); $input .prop('indeterminate', false) .prop('checked', true) ; }, unchecked: function() { module.verbose('Removing checked class'); $module .removeClass(className.indeterminate) .removeClass(className.checked) ; if(!module.is.indeterminate() && module.is.unchecked() ) { module.debug('Input is already unchecked'); return; } module.debug('Setting state to unchecked'); $input .prop('indeterminate', false) .prop('checked', false) ; }, indeterminate: function() { module.verbose('Setting class to indeterminate'); $module .addClass(className.indeterminate) ; if( module.is.indeterminate() ) { module.debug('Input is already indeterminate, skipping input property change'); return; } module.debug('Setting state to indeterminate'); $input .prop('indeterminate', true) ; }, determinate: function() { module.verbose('Removing indeterminate class'); $module .removeClass(className.indeterminate) ; if( module.is.determinate() ) { module.debug('Input is already determinate, skipping input property change'); return; } module.debug('Setting state to determinate'); $input .prop('indeterminate', false) ; }, disabled: function() { module.verbose('Setting class to disabled'); $module .addClass(className.disabled) ; if( module.is.disabled() ) { module.debug('Input is already disabled, skipping input property change'); return; } module.debug('Setting state to disabled'); $input .prop('disabled', 'disabled') ; }, enabled: function() { module.verbose('Removing disabled class'); $module.removeClass(className.disabled); if( module.is.enabled() ) { module.debug('Input is already enabled, skipping input property change'); return; } module.debug('Setting state to enabled'); $input .prop('disabled', false) ; }, tabbable: function() { module.verbose('Adding tabindex to checkbox'); if( $input.attr('tabindex') === undefined) { $input.attr('tabindex', 0); } } }, remove: { initialLoad: function() { initialLoad = false; } }, trigger: { change: function() { var inputElement = $input[0] ; if(inputElement) { var events = document.createEvent('HTMLEvents'); module.verbose('Triggering native change event'); events.initEvent('change', true, false); inputElement.dispatchEvent(events); } } }, create: { label: function() { if($input.prevAll(selector.label).length > 0) { $input.prev(selector.label).detach().insertAfter($input); module.debug('Moving existing label', $label); } else if( !module.has.label() ) { $label = $('