clipbucket/upload/styles/cb_28/theme/js/jcf_new/jcf.js

453 lines
13 KiB
JavaScript
Raw Normal View History

/*!
* JavaScript Custom Forms
*
* Copyright 2014-2015 PSD2HTML - http://psd2html.com/jcf
* Released under the MIT license (LICENSE.txt)
*
* Version: 1.2.1
*/
;(function(root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('jquery'));
} else {
root.jcf = factory(jQuery);
}
}(this, function($) {
'use strict';
// define version
var version = '1.2.1';
// private variables
var customInstances = [];
// default global options
var commonOptions = {
optionsKey: 'jcf',
dataKey: 'jcf-instance',
rtlClass: 'jcf-rtl',
focusClass: 'jcf-focus',
pressedClass: 'jcf-pressed',
disabledClass: 'jcf-disabled',
hiddenClass: 'jcf-hidden',
resetAppearanceClass: 'jcf-reset-appearance',
unselectableClass: 'jcf-unselectable'
};
// detect device type
var isTouchDevice = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
isWinPhoneDevice = /Windows Phone/.test(navigator.userAgent);
commonOptions.isMobileDevice = !!(isTouchDevice || isWinPhoneDevice);
// create global stylesheet if custom forms are used
var createStyleSheet = function() {
var styleTag = $('<style>').appendTo('head'),
styleSheet = styleTag.prop('sheet') || styleTag.prop('styleSheet');
// crossbrowser style handling
var addCSSRule = function(selector, rules, index) {
if (styleSheet.insertRule) {
styleSheet.insertRule(selector + '{' + rules + '}', index);
} else {
styleSheet.addRule(selector, rules, index);
}
};
// add special rules
addCSSRule('.' + commonOptions.hiddenClass, 'position:absolute !important;left:-9999px !important;height:1px !important;width:1px !important;margin:0 !important;border-width:0 !important;-webkit-appearance:none;-moz-appearance:none;appearance:none');
addCSSRule('.' + commonOptions.rtlClass + ' .' + commonOptions.hiddenClass, 'right:-9999px !important; left: auto !important');
addCSSRule('.' + commonOptions.unselectableClass, '-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0);');
addCSSRule('.' + commonOptions.resetAppearanceClass, 'background: none; border: none; -webkit-appearance: none; appearance: none; opacity: 0; filter: alpha(opacity=0);');
// detect rtl pages
var html = $('html'), body = $('body');
if (html.css('direction') === 'rtl' || body.css('direction') === 'rtl') {
html.addClass(commonOptions.rtlClass);
}
// handle form reset event
html.on('reset', function() {
setTimeout(function() {
api.refreshAll();
}, 0);
});
// mark stylesheet as created
commonOptions.styleSheetCreated = true;
};
// simplified pointer events handler
(function() {
var pointerEventsSupported = navigator.pointerEnabled || navigator.msPointerEnabled,
touchEventsSupported = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
eventList, eventMap = {}, eventPrefix = 'jcf-';
// detect events to attach
if (pointerEventsSupported) {
eventList = {
pointerover: navigator.pointerEnabled ? 'pointerover' : 'MSPointerOver',
pointerdown: navigator.pointerEnabled ? 'pointerdown' : 'MSPointerDown',
pointermove: navigator.pointerEnabled ? 'pointermove' : 'MSPointerMove',
pointerup: navigator.pointerEnabled ? 'pointerup' : 'MSPointerUp'
};
} else {
eventList = {
pointerover: 'mouseover',
pointerdown: 'mousedown' + (touchEventsSupported ? ' touchstart' : ''),
pointermove: 'mousemove' + (touchEventsSupported ? ' touchmove' : ''),
pointerup: 'mouseup' + (touchEventsSupported ? ' touchend' : '')
};
}
// create event map
$.each(eventList, function(targetEventName, fakeEventList) {
$.each(fakeEventList.split(' '), function(index, fakeEventName) {
eventMap[fakeEventName] = targetEventName;
});
});
// jQuery event hooks
$.each(eventList, function(eventName, eventHandlers) {
eventHandlers = eventHandlers.split(' ');
$.event.special[eventPrefix + eventName] = {
setup: function() {
var self = this;
$.each(eventHandlers, function(index, fallbackEvent) {
if (self.addEventListener) self.addEventListener(fallbackEvent, fixEvent, false);
else self['on' + fallbackEvent] = fixEvent;
});
},
teardown: function() {
var self = this;
$.each(eventHandlers, function(index, fallbackEvent) {
if (self.addEventListener) self.removeEventListener(fallbackEvent, fixEvent, false);
else self['on' + fallbackEvent] = null;
});
}
};
});
// check that mouse event are not simulated by mobile browsers
var lastTouch = null;
var mouseEventSimulated = function(e) {
var dx = Math.abs(e.pageX - lastTouch.x),
dy = Math.abs(e.pageY - lastTouch.y),
rangeDistance = 25;
if (dx <= rangeDistance && dy <= rangeDistance) {
return true;
}
};
// normalize event
var fixEvent = function(e) {
var origEvent = e || window.event,
touchEventData = null,
targetEventName = eventMap[origEvent.type];
e = $.event.fix(origEvent);
e.type = eventPrefix + targetEventName;
if (origEvent.pointerType) {
switch (origEvent.pointerType) {
case 2: e.pointerType = 'touch'; break;
case 3: e.pointerType = 'pen'; break;
case 4: e.pointerType = 'mouse'; break;
default: e.pointerType = origEvent.pointerType;
}
} else {
e.pointerType = origEvent.type.substr(0, 5); // "mouse" or "touch" word length
}
if (!e.pageX && !e.pageY) {
touchEventData = origEvent.changedTouches ? origEvent.changedTouches[0] : origEvent;
e.pageX = touchEventData.pageX;
e.pageY = touchEventData.pageY;
}
if (origEvent.type === 'touchend') {
lastTouch = { x: e.pageX, y: e.pageY };
}
if (e.pointerType === 'mouse' && lastTouch && mouseEventSimulated(e)) {
return;
} else {
return ($.event.dispatch || $.event.handle).call(this, e);
}
};
}());
// custom mousewheel/trackpad handler
(function() {
var wheelEvents = ('onwheel' in document || document.documentMode >= 9 ? 'wheel' : 'mousewheel DOMMouseScroll').split(' '),
shimEventName = 'jcf-mousewheel';
$.event.special[shimEventName] = {
setup: function() {
var self = this;
$.each(wheelEvents, function(index, fallbackEvent) {
if (self.addEventListener) self.addEventListener(fallbackEvent, fixEvent, false);
else self['on' + fallbackEvent] = fixEvent;
});
},
teardown: function() {
var self = this;
$.each(wheelEvents, function(index, fallbackEvent) {
if (self.addEventListener) self.removeEventListener(fallbackEvent, fixEvent, false);
else self['on' + fallbackEvent] = null;
});
}
};
var fixEvent = function(e) {
var origEvent = e || window.event;
e = $.event.fix(origEvent);
e.type = shimEventName;
// old wheel events handler
if ('detail' in origEvent) { e.deltaY = -origEvent.detail; }
if ('wheelDelta' in origEvent) { e.deltaY = -origEvent.wheelDelta; }
if ('wheelDeltaY' in origEvent) { e.deltaY = -origEvent.wheelDeltaY; }
if ('wheelDeltaX' in origEvent) { e.deltaX = -origEvent.wheelDeltaX; }
// modern wheel event handler
if ('deltaY' in origEvent) {
e.deltaY = origEvent.deltaY;
}
if ('deltaX' in origEvent) {
e.deltaX = origEvent.deltaX;
}
// handle deltaMode for mouse wheel
e.delta = e.deltaY || e.deltaX;
if (origEvent.deltaMode === 1) {
var lineHeight = 16;
e.delta *= lineHeight;
e.deltaY *= lineHeight;
e.deltaX *= lineHeight;
}
return ($.event.dispatch || $.event.handle).call(this, e);
};
}());
// extra module methods
var moduleMixin = {
// provide function for firing native events
fireNativeEvent: function(elements, eventName) {
$(elements).each(function() {
var element = this, eventObject;
if (element.dispatchEvent) {
eventObject = document.createEvent('HTMLEvents');
eventObject.initEvent(eventName, true, true);
element.dispatchEvent(eventObject);
} else if (document.createEventObject) {
eventObject = document.createEventObject();
eventObject.target = element;
element.fireEvent('on' + eventName, eventObject);
}
});
},
// bind event handlers for module instance (functions beggining with "on")
bindHandlers: function() {
var self = this;
$.each(self, function(propName, propValue) {
if (propName.indexOf('on') === 0 && $.isFunction(propValue)) {
// dont use $.proxy here because it doesn't create unique handler
self[propName] = function() {
return propValue.apply(self, arguments);
};
}
});
}
};
// public API
var api = {
version: version,
modules: {},
getOptions: function() {
return $.extend({}, commonOptions);
},
setOptions: function(moduleName, moduleOptions) {
if (arguments.length > 1) {
// set module options
if (this.modules[moduleName]) {
$.extend(this.modules[moduleName].prototype.options, moduleOptions);
}
} else {
// set common options
$.extend(commonOptions, moduleName);
}
},
addModule: function(proto) {
// proto is factory function
if ($.isFunction(proto)) {
proto = proto($, window);
}
// add module to list
var Module = function(options) {
// save instance to collection
if (!options.element.data(commonOptions.dataKey)) {
options.element.data(commonOptions.dataKey, this);
}
customInstances.push(this);
// save options
this.options = $.extend({}, commonOptions, this.options, getInlineOptions(options.element), options);
// bind event handlers to instance
this.bindHandlers();
// call constructor
this.init.apply(this, arguments);
};
// parse options from HTML attribute
var getInlineOptions = function(element) {
var dataOptions = element.data(commonOptions.optionsKey),
attrOptions = element.attr(commonOptions.optionsKey);
if (dataOptions) {
return dataOptions;
} else if (attrOptions) {
try {
return $.parseJSON(attrOptions);
} catch (e) {
// ignore invalid attributes
}
}
};
// set proto as prototype for new module
Module.prototype = proto;
// add mixin methods to module proto
$.extend(proto, moduleMixin);
if (proto.plugins) {
$.each(proto.plugins, function(pluginName, plugin) {
$.extend(plugin.prototype, moduleMixin);
});
}
// override destroy method
var originalDestroy = Module.prototype.destroy;
Module.prototype.destroy = function() {
this.options.element.removeData(this.options.dataKey);
for (var i = customInstances.length - 1; i >= 0; i--) {
if (customInstances[i] === this) {
customInstances.splice(i, 1);
break;
}
}
if (originalDestroy) {
originalDestroy.apply(this, arguments);
}
};
// save module to list
this.modules[proto.name] = Module;
},
getInstance: function(element) {
return $(element).data(commonOptions.dataKey);
},
replace: function(elements, moduleName, customOptions) {
var self = this,
instance;
if (!commonOptions.styleSheetCreated) {
createStyleSheet();
}
$(elements).each(function() {
var moduleOptions,
element = $(this);
instance = element.data(commonOptions.dataKey);
if (instance) {
instance.refresh();
} else {
if (!moduleName) {
$.each(self.modules, function(currentModuleName, module) {
if (module.prototype.matchElement.call(module.prototype, element)) {
moduleName = currentModuleName;
return false;
}
});
}
if (moduleName) {
moduleOptions = $.extend({ element: element }, customOptions);
instance = new self.modules[moduleName](moduleOptions);
}
}
});
return instance;
},
refresh: function(elements) {
$(elements).each(function() {
var instance = $(this).data(commonOptions.dataKey);
if (instance) {
instance.refresh();
}
});
},
destroy: function(elements) {
$(elements).each(function() {
var instance = $(this).data(commonOptions.dataKey);
if (instance) {
instance.destroy();
}
});
},
replaceAll: function(context) {
var self = this;
$.each(this.modules, function(moduleName, module) {
$(module.prototype.selector, context).each(function() {
if (this.className.indexOf('jcf-ignore') < 0) {
self.replace(this, moduleName);
}
});
});
},
refreshAll: function(context) {
if (context) {
$.each(this.modules, function(moduleName, module) {
$(module.prototype.selector, context).each(function() {
var instance = $(this).data(commonOptions.dataKey);
if (instance) {
instance.refresh();
}
});
});
} else {
for (var i = customInstances.length - 1; i >= 0; i--) {
customInstances[i].refresh();
}
}
},
destroyAll: function(context) {
if (context) {
$.each(this.modules, function(moduleName, module) {
$(module.prototype.selector, context).each(function(index, element) {
var instance = $(element).data(commonOptions.dataKey);
if (instance) {
instance.destroy();
}
});
});
} else {
while (customInstances.length) {
customInstances[0].destroy();
}
}
}
};
return api;
}));