diff options
| author | Luminarys <postmaster@gensok.io> | 2016-01-17 11:54:39 +0100 |
|---|---|---|
| committer | Juuso Lapinlampi <wub@partyvan.eu> | 2016-01-17 17:26:03 +0100 |
| commit | bfa395944520c13064a26f3f94487131598503ab (patch) | |
| tree | f1ff55d90862471e336a5162d3490a542ecf63cc /js | |
| parent | 4dbb7930b80f7af0307922544ea9b3ccab99cb2c (diff) | |
Replace old JavaScript with simpler vanilla version
Pomf's old JavaScript is a frozen dependency hell with an old version of
Zepto. The amount of code included has reached the point of
unmaintainability, where nobody really understands very well how the
code works.
This code gets rid of all the previous client-side JavaScript and
replaces it with new, vanilla JavaScript. No dependencies required. This
new JavaScript should be quite a bit faster too.
Browser backwards compatibility has not been tested yet, however it is
expected that IE10 and later will work, improving our current backwards
compatibility.
Reviewed-by: Eliot Whalan <ewhal@pantsu.cat>
[wub@partyvan.eu: Added Git commit message and fixed Git branching.
Reverted /* outline-offset: -2px;*/ removal.]
Signed-off-by: Juuso Lapinlampi <wub@partyvan.eu>
Diffstat (limited to 'js')
| -rw-r--r-- | js/app.js | 148 | ||||
| -rw-r--r-- | js/cabinet.js | 66 | ||||
| -rw-r--r-- | js/cheesesteak.js | 232 | ||||
| -rw-r--r-- | js/pomf.js | 175 | ||||
| -rw-r--r-- | js/zepto.js | 1035 |
5 files changed, 148 insertions, 1508 deletions
diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..abb22ed --- /dev/null +++ b/js/app.js @@ -0,0 +1,148 @@ +function addRow(file) { + var row = document.createElement('li'); + + var name = document.createElement('span'); + name.textContent = file.name; + name.className = 'file-name'; + + var progressIndicator = document.createElement('span'); + progressIndicator.className = 'progress-percent'; + progressIndicator.textContent = '0%'; + + var progressBar = document.createElement('progress'); + progressBar.className = 'file-progress'; + progressBar.setAttribute("max", "100"); + progressBar.setAttribute("value", "0"); + + row.appendChild(name); + row.appendChild(progressBar); + row.appendChild(progressIndicator); + + document.getElementById('upload-filelist').appendChild(row); + return row; +} + +function handleUploadProgress(evt) { + var xhr = evt.target; + var bar = xhr.bar; + var percentIndicator = xhr.percent; + + if (evt.lengthComputable) { + var progressPercent = Math.floor((evt.loaded / evt.total) * 100); + bar.setAttribute("value", progressPercent); + percentIndicator.textContent = progressPercent + '%'; + } +} + +function handleUploadComplete(evt) { + var xhr = evt.target; + var bar = xhr.bar; + var row = xhr.row; + var percentIndicator = xhr.percent; + + percentIndicator.style.visibility = "hidden"; + bar.style.visibility = "hidden"; + row.removeChild(bar); + row.removeChild(percentIndicator); + var respStatus = xhr.status; + + var url = document.createElement('span'); + url.className = 'file-url'; + row.appendChild(url); + + var link = document.createElement('a'); + if (respStatus === 200) { + var response = JSON.parse(xhr.responseText); + if (response.success) { + link.textContent = response.files[0].url.replace(/.*?:\/\//g, ""); + link.href = response.files[0].url; + url.appendChild(link); + } else { + bar.innerHTML = 'Error: ' + response.reason; + } + } else if (respStatus === 413) { + link.textContent = 'File Too big!'; + url.appendChild(link); + } else { + link.textContent = 'Server error!'; + url.appendChild(link); + } +} + +function uploadFile(file, row) { + var bar = row.querySelector('.file-progress'); + var percentIndicator = row.querySelector('.progress-percent'); + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/upload.php'); + + xhr["row"] = row; + xhr["bar"] = bar; + xhr["percent"] = percentIndicator; + xhr.upload["bar"] = bar; + xhr.upload["percent"] = percentIndicator; + + xhr.addEventListener('load', handleUploadComplete, false); + xhr.upload.onprogress = handleUploadProgress; + + var form = new FormData(); + form.append('files[]', file); + xhr.send(form); +} + +function stopDefaultEvent(evt) { + evt.stopPropagation(); + evt.preventDefault(); +} + +function handleDrag(state, element, evt) { + stopDefaultEvent(evt); + if (state.dragCount == 1) { + element.textContent = "Drop it here~"; + } + state.dragCount += 1; +} + +function handleDragAway(state, element, evt) { + stopDefaultEvent(evt); + state.dragCount -= 1; + if (state.dragCount == 0) { + element.textContent = "Select or drop file(s)"; + } +} + +function handleDragDrop(state, element, evt) { + stopDefaultEvent(evt); + handleDragAway(state, element, evt); + var len = evt.dataTransfer.files.length; + for (var i = 0; i < len; i++) { + var file = evt.dataTransfer.files[i]; + var row = addRow(file); + uploadFile(file, row); + } +} + +function uploadFiles(evt) { + var len = evt.target.files.length; + for (var i = 0; i < len; i++) { + var file = evt.target.files[i]; + var row = addRow(file); + uploadFile(file, row); + } +} + +function selectFiles(target, evt) { + stopDefaultEvent(evt); + target.click(); +} + +var state = { dragCount: 0 }; +var uploadButton = document.getElementById('upload-btn'); +window.addEventListener('dragenter', handleDrag.bind(this, state, uploadButton), false); +window.addEventListener('dragleave', handleDragAway.bind(this, state, uploadButton), false); +window.addEventListener('drop', handleDragAway.bind(this, state, uploadButton), false); +window.addEventListener('dragover', stopDefaultEvent, false); + +var uploadInput = document.getElementById('upload-input'); +uploadInput.addEventListener('change', uploadFiles); +uploadButton.addEventListener('click', selectFiles.bind(this, uploadInput)); +uploadButton.addEventListener('drop', handleDragDrop.bind(this, state, uploadButton), false); diff --git a/js/cabinet.js b/js/cabinet.js deleted file mode 100644 index ba28a33..0000000 --- a/js/cabinet.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -(function(cabinet) { - cabinet.hasFileAPI = function hasFileAPI() { - return Boolean(window.FormData); - }; - - $.fn.cabinet = function(uploadInput) { - var $input = $(uploadInput); - - var _this = this; - var passthrough = function(outer, inner, prevent, fn) { - _this.on(outer, function(evt) { - if (inner === 'click') { - $input[0].click(); - } else { - $input.trigger(inner); - } - - if (prevent) { - evt.preventDefault(); - } - - if (fn) { - fn(evt); - } - }, false); - }; - - $input[0].filelist = Object.create(FileList); - - $input.on('change', function(evt) { - this.filelist = evt.target.files; - _this.change(); - }); - - passthrough('dragenter', 'dragenter', true); - passthrough('dragover', 'dragover', true); - passthrough('dragleave', 'dragleave', true); - passthrough('click', 'click', false); - passthrough('drop', 'dragleave', true, function(evt) { - $input[0].filelist = evt.dataTransfer.files; - _this.change(); - }); - }; -})($); diff --git a/js/cheesesteak.js b/js/cheesesteak.js deleted file mode 100644 index 6e20880..0000000 --- a/js/cheesesteak.js +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -(function() { - // Lightweight EventEmitter implementation - var EventEmitter = function() {}; - - // Utility to convert bytes into human units - var humanSize = { - get: function humanSize() { - var units = [ - 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB', - ]; - var base = Math.floor(Math.log(this.size) / Math.log(1024)); - return (this.size / Math.pow(1024, base)).toFixed(2) + ' ' + units[base]; - }, - }; - - var percentUploaded = { - get: function percentUploaded() { - return this.uploadedSize / this.size; - }, - }; - - // b gets merged as defaults for a - var merge = function merge(base, overlay) { - var out = base; - var key; - - for (key in overlay) { - if (typeof overlay[key] === 'object') { - out[key] = merge(base[key], overlay[key]); - } else { - out[key] = overlay[key]; - } - } - - return out; - }; - - var FileListUploader = function(url, files, opts) { - var request = opts || {}; - - this.url = url; - this.files = files; - this.opts = merge({ - field: 'files[]', - method: 'POST', - data: {}, - }, request); - }; - - EventEmitter.prototype.on = function(evt, fn) { - this._events = this._events || {}; - this._events[evt] = this._events[evt] || []; - this._events[evt].push(fn); - }; - - EventEmitter.prototype.off = function(evt, fn) { - if (!this.hasOwnProperty('_events') || evt in this._events === false) { - return; - } - - this._events[evt].splice(this._events[evt].indexOf(fn), 1); - }; - - EventEmitter.prototype.emit = function(evt) { - var i; - var len; - - // Check whether the event we want to emit actually exists - if (!this.hasOwnProperty('_events') || evt in this._events === false) { - return; - } - - len = this._events[evt].length; - - // Call all functions that are bound to the event - for (i = 0; i < len; i++) { - this._events[evt][i].apply(this, Array.prototype.slice.call(arguments, - 1)); - } - }; - - // Copy the stuff from Array to FileList - FileList.prototype.forEach = Array.prototype.forEach; - FileList.prototype.every = Array.prototype.every; - FileList.prototype.some = Array.prototype.some; - FileList.prototype.filter = Array.prototype.filter; - FileList.prototype.map = Array.prototype.map; - FileList.prototype.reduce = Array.prototype.reduce; - FileList.prototype.reduceRight = Array.prototype.reduceRight; - - Object.defineProperty(FileList.prototype, 'size', { - get: function getSize() { - return this.reduce(function(prev, curr) { - return prev + curr.size; - }, 0); - }, - }); - - Object.defineProperty(FileList.prototype, 'humanSize', humanSize); - Object.defineProperty(File.prototype, 'humanSize', humanSize); - - Object.defineProperty(FileList.prototype, 'percentUploaded', - percentUploaded); - Object.defineProperty(File.prototype, 'percentUploaded', percentUploaded); - - Object.defineProperty(FileList.prototype, 'uploadedSize', { - get: function getUploadedSize() { - return this.reduce(function(prev, curr) { - return prev + (curr.uploadedSize || 0); - }, 0); - }, - }); - - // Object URL stuff - Object.defineProperty(File.prototype, 'url', { - get: function getURL() { - return window.URL.createObjectURL(this); - }, - }); - File.revokeURL = function revokeURL(url) { - window.URL.revokeObjectURL(url); - }; - - FileListUploader.prototype = Object.create(EventEmitter.prototype); - FileListUploader.prototype.upload = function(cb) { - var opts = this.opts; - var files = this.files; - var _this = this; - var len = files.length; - var data = new FormData(); - var xhr = new XMLHttpRequest(); - var i; - - if (cb) { - this.on('uploadcomplete', cb); - } - - files.forEach(function(file) { - data.append(opts.field, file); - }); - - xhr.open(opts.method, this.url, true); - - function initProgressBar() { - for (i = 0; i < len; i++) { - files[i].uploadedSize = 0; - } - } - - xhr.upload.addEventListener('loadstart', initProgressBar); - - function updateProgressBar(evt) { - var size; - - if (evt.lengthComputable) { - size = evt.loaded; - - /** - * We know the size of the files, the order they're in, and how - * much we've uploaded. Based on this data, we can do some magic - * to figure out which file we're on, and how much we've uploaded - * of that file. - */ - - // TODO: This math has trouble on later uploads. Leak somewhere. - for (i = 0; i < len; i++) { - files[i].uploadedSize = Math.min(size, files[i].size); - size -= files[i].uploadedSize; - if (size <= 0) { - files.current = files[i]; - size = 0; - } - } - } - - _this.emit('uploadprogress', evt, files); - } - - xhr.upload.addEventListener('progress', updateProgressBar, false); - - // The upload is complete, now tell the user to wait for URLs. - function postUpload(evt) { - _this.emit('uploadcomplete', evt); - } - - xhr.upload.addEventListener('load', postUpload); - - // Tell the browser the upload completed and wait for response. - function postProgress(evt) { - _this.emit('progress', evt); - } - - xhr.addEventListener('progress', postProgress); - - // Send a success/error response. Nothing more to do. - function uploadFinished(evt) { - _this.emit('load', evt, xhr.responseText); - } - - xhr.addEventListener('load', uploadFinished); - - xhr.send(data); - - // TODO: Add more event passthroughs, maybe abstract it into a helper? - }; - - FileList.prototype.upload = function uploadFileList(url, opts) { - return new FileListUploader(url, this, opts); - }; -})(); diff --git a/js/pomf.js b/js/pomf.js deleted file mode 100644 index cf91e35..0000000 --- a/js/pomf.js +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright (c) 2013 Peter Lejeck <peter.lejeck@gmail.com> - * Copyright (c) 2015 the Pantsu.cat developers <hostmaster@pantsu.cat> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -$(function() { - var $uploadInput = $('#upload-input'); - var $uploadBtn = $('#upload-btn'); - var $uploadFiles = $('#upload-filelist'); - - var $btnContent = ''; - - var MAX_SIZE = (function(node) { - var max = node.attr('data-max-size') || '10MiB'; - var num = parseInt(/([0-9,]+).*/.exec(max)[1].replace(',', ''), 10); - var unit = /(?:([KMGTPEZY])(i)?B|([BKMGTPEZY]))/.exec(max) || ['B', '', '']; - - var oneUnit = Math.pow( - (unit[2] === 'i' ? 1024 : 1000), - 'BKMGTPEZY'.indexOf(unit[1]) - ); - - return num * oneUnit; - })($uploadInput); - - var createRow = function(filename, size, extra) { - var $rowItem = $('<li class=file>'); - var $rowName = $('<span class=file-name>'); - var $rowProg = $('<div class="file-progress progress-outer">'); - var $rowSize = $('<span class=file-size>'); - var $rowUrl = $('<span class=file-url>'); - - $rowItem.addClass(extra || ''); - - $('<div class=progress-inner>').appendTo($rowProg); - - $rowItem.attr('data-filename', escape(filename)); - $rowName.text(filename); - $rowSize.text(size); - - $rowItem.append($rowName, $rowProg, $rowSize, $rowUrl); - - return $rowItem; - }; - - if (!$.hasFileAPI()) { - $('#no-file-api').show(); - $uploadBtn.hide(); - } - - $uploadBtn.cabinet($uploadInput); - - $uploadBtn.on('dragenter', function(evt) { - if (this === evt.target) { - $(this).addClass('drop'); - $btnContent = $(this).html(); - $(this).html('Drop it here~'); - } - }); - - $uploadBtn.on('drop', function() { - $(this).trigger('dragleave'); - }); - - $uploadBtn.on('dragleave', function(evt) { - var node = evt.target; - do { - if (node === this) { - $(this).removeClass('drop'); - $(this).html($btnContent); - break; - } - } while (node === node.parentNode); - }); - - $uploadBtn.on('change', function() { - var files = $uploadInput[0].filelist; - var totalRow = createRow('', files.humanSize, 'total'); - var $totalName = $('.file-name', totalRow); - var UPLOAD_ERR_MAX_SIZE = 'onii-chan y-your upload is t-too big…'; - var UPLOAD_ERR_FAILED = 'Something went wrong; try again later.'; - var up = files.upload('upload.php'); - - var eachRow = function(_files, fn) { - var hits = {}; - _files.forEach(function(file) { - var row = $($('li[data-filename="' + - escape(file.name) + '"]')[hits[file.name] || 0]); - ++hits[file.name]; - fn.call(row, row, file, _files); - }); - }; - - $uploadFiles.empty().removeClass('error completed'); - - files.forEach(function(file) { - createRow(file.name, file.humanSize).appendTo($uploadFiles); - }); - - totalRow.appendTo($uploadFiles); - - if (files.size > MAX_SIZE) { - $uploadFiles.addClass('error'); - $totalName.html(UPLOAD_ERR_MAX_SIZE); - return; - } - - up.on('uploadprogress', function(evt, _files) { - eachRow(_files, function(row, file) { - $('.progress-inner', row).width((file.percentUploaded * 100) + '%'); - }); - - $('.progress-inner', totalRow).width((_files.percentUploaded * 100) + '%'); - }); - - up.on('uploadcomplete', function() { - $('.progress-inner').width('100%'); - $totalName.html('Grabbing URLs…'); - }); - - up.on('load', function(evt, response) { - var res = JSON.parse(response); - - switch (evt.target.status) { - case 200: - if (!res.success) { - $uploadFiles.addClass('error'); - $totalName.text(UPLOAD_ERR_FAILED); - break; - } - - eachRow(res.files, function(row, file) { - var $link = $('<a>'); - - $link.attr('href', file.url) - .attr('target', '_BLANK') - .text(file.url.replace('http://', '').replace('https://', '')); - - $('.file-url', row).append($link); - }); - - $uploadFiles.addClass('completed'); - $totalName.text('Done!'); - break; - case 413: - $uploadFiles.addClass('error completed'); - $totalName.html(UPLOAD_ERR_MAX_SIZE); - break; - default: - $uploadFiles.addClass('error completed'); - $totalName.text(UPLOAD_ERR_FAILED); - } - }); - - up.upload(); - }); -}); diff --git a/js/zepto.js b/js/zepto.js deleted file mode 100644 index ab89703..0000000 --- a/js/zepto.js +++ /dev/null @@ -1,1035 +0,0 @@ -/* Zepto v1.0-7-g579f376 - zepto event - zeptojs.com/license */ - - -var Zepto = (function() { - var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, - document = window.document, - elementDisplay = {}, classCache = {}, - getComputedStyle = document.defaultView.getComputedStyle, - cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, - fragmentRE = /^\s*<(\w+|!)[^>]*>/, - tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, - rootNodeRE = /^(?:body|html)$/i, - - // special attributes that should be get/set via method calls - methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], - - adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], - table = document.createElement('table'), - tableRow = document.createElement('tr'), - containers = { - 'tr': document.createElement('tbody'), - 'tbody': table, 'thead': table, 'tfoot': table, - 'td': tableRow, 'th': tableRow, - '*': document.createElement('div') - }, - readyRE = /complete|loaded|interactive/, - classSelectorRE = /^\.([\w-]+)$/, - idSelectorRE = /^#([\w-]*)$/, - tagSelectorRE = /^[\w-]+$/, - class2type = {}, - toString = class2type.toString, - zepto = {}, - camelize, uniq, - tempParent = document.createElement('div') - - zepto.matches = function(element, selector) { - if (!element || element.nodeType !== 1) return false - var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || - element.oMatchesSelector || element.matchesSelector - if (matchesSelector) return matchesSelector.call(element, selector) - // fall back to performing a selector: - var match, parent = element.parentNode, temp = !parent - if (temp) (parent = tempParent).appendChild(element) - match = ~zepto.qsa(parent, selector).indexOf(element) - temp && tempParent.removeChild(element) - return match - } - - function type(obj) { - return obj == null ? String(obj) : - class2type[toString.call(obj)] || "object" - } - - function isFunction(value) { return type(value) == "function" } - function isWindow(obj) { return obj != null && obj == obj.window } - function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } - function isObject(obj) { return type(obj) == "object" } - function isPlainObject(obj) { - return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype - } - function isArray(value) { return value instanceof Array } - function likeArray(obj) { return typeof obj.length == 'number' } - - function compact(array) { return filter.call(array, function(item){ return item != null }) } - function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } - camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } - function dasherize(str) { - return str.replace(/::/g, '/') - .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') - .replace(/([a-z\d])([A-Z])/g, '$1_$2') - .replace(/_/g, '-') - .toLowerCase() - } - uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } - - function classRE(name) { - return name in classCache ? - classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) - } - - function maybeAddPx(name, value) { - return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value - } - - function defaultDisplay(nodeName) { - var element, display - if (!elementDisplay[nodeName]) { - element = document.createElement(nodeName) - document.body.appendChild(element) - display = getComputedStyle(element, '').getPropertyValue("display") - element.parentNode.removeChild(element) - display == "none" && (display = "block") - elementDisplay[nodeName] = display - } - return elementDisplay[nodeName] - } - - function children(element) { - return 'children' in element ? - slice.call(element.children) : - $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) - } - - // `$.zepto.fragment` takes a html string and an optional tag name - // to generate DOM nodes nodes from the given html string. - // The generated DOM nodes are returned as an array. - // This function can be overriden in plugins for example to make - // it compatible with browsers that don't support the DOM fully. - zepto.fragment = function(html, name, properties) { - if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>") - if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 - if (!(name in containers)) name = '*' - - var nodes, dom, container = containers[name] - container.innerHTML = '' + html - dom = $.each(slice.call(container.childNodes), function(){ - container.removeChild(this) - }) - if (isPlainObject(properties)) { - nodes = $(dom) - $.each(properties, function(key, value) { - if (methodAttributes.indexOf(key) > -1) nodes[key](value) - else nodes.attr(key, value) - }) - } - return dom - } - - // `$.zepto.Z` swaps out the prototype of the given `dom` array - // of nodes with `$.fn` and thus supplying all the Zepto functions - // to the array. Note that `__proto__` is not supported on Internet - // Explorer. This method can be overriden in plugins. - zepto.Z = function(dom, selector) { - dom = dom || [] - dom.__proto__ = $.fn - dom.selector = selector || '' - return dom - } - - // `$.zepto.isZ` should return `true` if the given object is a Zepto - // collection. This method can be overriden in plugins. - zepto.isZ = function(object) { - return object instanceof zepto.Z - } - - // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and - // takes a CSS selector and an optional context (and handles various - // special cases). - // This method can be overriden in plugins. - zepto.init = function(selector, context) { - // If nothing given, return an empty Zepto collection - if (!selector) return zepto.Z() - // If a function is given, call it when the DOM is ready - else if (isFunction(selector)) return $(document).ready(selector) - // If a Zepto collection is given, juts return it - else if (zepto.isZ(selector)) return selector - else { - var dom - // normalize array if an array of nodes is given - if (isArray(selector)) dom = compact(selector) - // Wrap DOM nodes. If a plain object is given, duplicate it. - else if (isObject(selector)) - dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null - // If it's a html fragment, create nodes from it - else if (fragmentRE.test(selector)) - dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null - // If there's a context, create a collection on that context first, and select - // nodes from there - else if (context !== undefined) return $(context).find(selector) - // And last but no least, if it's a CSS selector, use it to select nodes. - else dom = zepto.qsa(document, selector) - // create a new Zepto collection from the nodes found - return zepto.Z(dom, selector) - } - } - - // `$` will be the base `Zepto` object. When calling this - // function just call `$.zepto.init, which makes the implementation - // details of selecting nodes and creating Zepto collections - // patchable in plugins. - $ = function(selector, context){ - return zepto.init(selector, context) - } - - function extend(target, source, deep) { - for (key in source) - if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { - if (isPlainObject(source[key]) && !isPlainObject(target[key])) - target[key] = {} - if (isArray(source[key]) && !isArray(target[key])) - target[key] = [] - extend(target[key], source[key], deep) - } - else if (source[key] !== undefined) target[key] = source[key] - } - - // Copy all but undefined properties from one or more - // objects to the `target` object. - $.extend = function(target){ - var deep, args = slice.call(arguments, 1) - if (typeof target == 'boolean') { - deep = target - target = args.shift() - } - args.forEach(function(arg){ extend(target, arg, deep) }) - return target - } - - // `$.zepto.qsa` is Zepto's CSS selector implementation which - // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. - // This method can be overriden in plugins. - zepto.qsa = function(element, selector){ - var found - return (isDocument(element) && idSelectorRE.test(selector)) ? - ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) : - (element.nodeType !== 1 && element.nodeType !== 9) ? [] : - slice.call( - classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) : - tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) : - element.querySelectorAll(selector) - ) - } - - function filtered(nodes, selector) { - return selector == null ? $(nodes) : $(nodes).filter(selector) - } - - $.contains = function(parent, node) { - return parent !== node && parent.contains(node) - } - - function funcArg(context, arg, idx, payload) { - return isFunction(arg) ? arg.call(context, idx, payload) : arg - } - - function setAttribute(node, name, value) { - value == null ? node.removeAttribute(name) : node.setAttribute(name, value) - } - - // access className property while respecting SVGAnimatedString - function className(node, value){ - var klass = node.className, - svg = klass && klass.baseVal !== undefined - - if (value === undefined) return svg ? klass.baseVal : klass - svg ? (klass.baseVal = value) : (node.className = value) - } - - // "true" => true - // "false" => false - // "null" => null - // "42" => 42 - // "42.5" => 42.5 - // JSON => parse if valid - // String => self - function deserializeValue(value) { - var num - try { - return value ? - value == "true" || - ( value == "false" ? false : - value == "null" ? null : - !isNaN(num = Number(value)) ? num : - /^[\[\{]/.test(value) ? $.parseJSON(value) : - value ) - : value - } catch(e) { - return value - } - } - - $.type = type - $.isFunction = isFunction - $.isWindow = isWindow - $.isArray = isArray - $.isPlainObject = isPlainObject - - $.isEmptyObject = function(obj) { - var name - for (name in obj) return false - return true - } - - $.inArray = function(elem, array, i){ - return emptyArray.indexOf.call(array, elem, i) - } - - $.camelCase = camelize - $.trim = function(str) { - return str == null ? "" : String.prototype.trim.call(str) - } - - // plugin compatibility - $.uuid = 0 - $.support = { } - $.expr = { } - - $.map = function(elements, callback){ - var value, values = [], i, key - if (likeArray(elements)) - for (i = 0; i < elements.length; i++) { - value = callback(elements[i], i) - if (value != null) values.push(value) - } - else - for (key in elements) { - value = callback(elements[key], key) - if (value != null) values.push(value) - } - return flatten(values) - } - - $.each = function(elements, callback){ - var i, key - if (likeArray(elements)) { - for (i = 0; i < elements.length; i++) - if (callback.call(elements[i], i, elements[i]) === false) return elements - } else { - for (key in elements) - if (callback.call(elements[key], key, elements[key]) === false) return elements - } - - return elements - } - - $.grep = function(elements, callback){ - return filter.call(elements, callback) - } - - if (window.JSON) $.parseJSON = JSON.parse - - // Populate the class2type map - $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase() - }) - - // Define methods that will be available on all - // Zepto collections - $.fn = { - // Because a collection acts like an array - // copy over these useful array functions. - forEach: emptyArray.forEach, - reduce: emptyArray.reduce, - push: emptyArray.push, - sort: emptyArray.sort, - indexOf: emptyArray.indexOf, - concat: emptyArray.concat, - - // `map` and `slice` in the jQuery API work differently - // from their array counterparts - map: function(fn){ - return $($.map(this, function(el, i){ return fn.call(el, i, el) })) - }, - slice: function(){ - return $(slice.apply(this, arguments)) - }, - - ready: function(callback){ - if (readyRE.test(document.readyState)) callback($) - else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) - return this - }, - get: function(idx){ - return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] - }, - toArray: function(){ return this.get() }, - size: function(){ - return this.length - }, - remove: function(){ - return this.each(function(){ - if (this.parentNode != null) - this.parentNode.removeChild(this) - }) - }, - each: function(callback){ - emptyArray.every.call(this, function(el, idx){ - return callback.call(el, idx, el) !== false - }) - return this - }, - filter: function(selector){ - if (isFunction(selector)) return this.not(this.not(selector)) - return $(filter.call(this, function(element){ - return zepto.matches(element, selector) - })) - }, - add: function(selector,context){ - return $(uniq(this.concat($(selector,context)))) - }, - is: function(selector){ - return this.length > 0 && zepto.matches(this[0], selector) - }, - not: function(selector){ - var nodes=[] - if (isFunction(selector) && selector.call !== undefined) - this.each(function(idx){ - if (!selector.call(this,idx)) nodes.push(this) - }) - else { - var excludes = typeof selector == 'string' ? this.filter(selector) : - (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) - this.forEach(function(el){ - if (excludes.indexOf(el) < 0) nodes.push(el) - }) - } - return $(nodes) - }, - has: function(selector){ - return this.filter(function(){ - return isObject(selector) ? - $.contains(this, selector) : - $(this).find(selector).size() - }) - }, - eq: function(idx){ - return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) - }, - first: function(){ - var el = this[0] - return el && !isObject(el) ? el : $(el) - }, - last: function(){ - var el = this[this.length - 1] - return el && !isObject(el) ? el : $(el) - }, - find: function(selector){ - var result, $this = this - if (typeof selector == 'object') - result = $(selector).filter(function(){ - var node = this - return emptyArray.some.call($this, function(parent){ - return $.contains(parent, node) - }) - }) - else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) - else result = this.map(function(){ return zepto.qsa(this, selector) }) - return result - }, - closest: function(selector, context){ - var node = this[0], collection = false - if (typeof selector == 'object') collection = $(selector) - while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) - node = node !== context && !isDocument(node) && node.parentNode - return $(node) - }, - parents: function(selector){ - var ancestors = [], nodes = this - while (nodes.length > 0) - nodes = $.map(nodes, function(node){ - if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { - ancestors.push(node) - return node - } - }) - return filtered(ancestors, selector) - }, - parent: function(selector){ - return filtered(uniq(this.pluck('parentNode')), selector) - }, - children: function(selector){ - return filtered(this.map(function(){ return children(this) }), selector) - }, - contents: function() { - return this.map(function() { return slice.call(this.childNodes) }) - }, - siblings: function(selector){ - return filtered(this.map(function(i, el){ - return filter.call(children(el.parentNode), function(child){ return child!==el }) - }), selector) - }, - empty: function(){ - return this.each(function(){ this.innerHTML = '' }) - }, - // `pluck` is borrowed from Prototype.js - pluck: function(property){ - return $.map(this, function(el){ return el[property] }) - }, - show: function(){ - return this.each(function(){ - this.style.display == "none" && (this.style.display = null) - if (getComputedStyle(this, '').getPropertyValue("display") == "none") - this.style.display = defaultDisplay(this.nodeName) - }) - }, - replaceWith: function(newContent){ - return this.before(newContent).remove() - }, - wrap: function(structure){ - var func = isFunction(structure) - if (this[0] && !func) - var dom = $(structure).get(0), - clone = dom.parentNode || this.length > 1 - - return this.each(function(index){ - $(this).wrapAll( - func ? structure.call(this, index) : - clone ? dom.cloneNode(true) : dom - ) - }) - }, - wrapAll: function(structure){ - if (this[0]) { - $(this[0]).before(structure = $(structure)) - var children - // drill down to the inmost element - while ((children = structure.children()).length) structure = children.first() - $(structure).append(this) - } - return this - }, - wrapInner: function(structure){ - var func = isFunction(structure) - return this.each(function(index){ - var self = $(this), contents = self.contents(), - dom = func ? structure.call(this, index) : structure - contents.length ? contents.wrapAll(dom) : self.append(dom) - }) - }, - unwrap: function(){ - this.parent().each(function(){ - $(this).replaceWith($(this).children()) - }) - return this - }, - clone: function(){ - return this.map(function(){ return this.cloneNode(true) }) - }, - hide: function(){ - return this.css("display", "none") - }, - toggle: function(setting){ - return this.each(function(){ - var el = $(this) - ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() - }) - }, - prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, - next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, - html: function(html){ - return html === undefined ? - (this.length > 0 ? this[0].innerHTML : null) : - this.each(function(idx){ - var originHtml = this.innerHTML - $(this).empty().append( funcArg(this, html, idx, originHtml) ) - }) - }, - text: function(text){ - return text === undefined ? - (this.length > 0 ? this[0].textContent : null) : - this.each(function(){ this.textContent = text }) - }, - attr: function(name, value){ - var result - return (typeof name == 'string' && value === undefined) ? - (this.length == 0 || this[0].nodeType !== 1 ? undefined : - (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : - (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result - ) : - this.each(function(idx){ - if (this.nodeType !== 1) return - if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) - else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) - }) - }, - removeAttr: function(name){ - return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) }) - }, - prop: function(name, value){ - return (value === undefined) ? - (this[0] && this[0][name]) : - this.each(function(idx){ - this[name] = funcArg(this, value, idx, this[name]) - }) - }, - data: function(name, value){ - var data = this.attr('data-' + dasherize(name), value) - return data !== null ? deserializeValue(data) : undefined - }, - val: function(value){ - return (value === undefined) ? - (this[0] && (this[0].multiple ? - $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') : - this[0].value) - ) : - this.each(function(idx){ - this.value = funcArg(this, value, idx, this.value) - }) - }, - offset: function(coordinates){ - if (coordinates) return this.each(function(index){ - var $this = $(this), - coords = funcArg(this, coordinates, index, $this.offset()), - parentOffset = $this.offsetParent().offset(), - props = { - top: coords.top - parentOffset.top, - left: coords.left - parentOffset.left - } - - if ($this.css('position') == 'static') props['position'] = 'relative' - $this.css(props) - }) - if (this.length==0) return null - var obj = this[0].getBoundingClientRect() - return { - left: obj.left + window.pageXOffset, - top: obj.top + window.pageYOffset, - width: Math.round(obj.width), - height: Math.round(obj.height) - } - }, - css: function(property, value){ - if (arguments.length < 2 && typeof property == 'string') - return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property)) - - var css = '' - if (type(property) == 'string') { - if (!value && value !== 0) - this.each(function(){ this.style.removeProperty(dasherize(property)) }) - else - css = dasherize(property) + ":" + maybeAddPx(property, value) - } else { - for (key in property) - if (!property[key] && property[key] !== 0) - this.each(function(){ this.style.removeProperty(dasherize(key)) }) - else - css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' - } - - return this.each(function(){ this.style.cssText += ';' + css }) - }, - index: function(element){ - return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) - }, - hasClass: function(name){ - return emptyArray.some.call(this, function(el){ - return this.test(className(el)) - }, classRE(name)) - }, - addClass: function(name){ - return this.each(function(idx){ - classList = [] - var cls = className(this), newName = funcArg(this, name, idx, cls) - newName.split(/\s+/g).forEach(function(klass){ - if (!$(this).hasClass(klass)) classList.push(klass) - }, this) - classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) - }) - }, - removeClass: function(name){ - return this.each(function(idx){ - if (name === undefined) return className(this, '') - classList = className(this) - funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ - classList = classList.replace(classRE(klass), " ") - }) - className(this, classList.trim()) - }) - }, - toggleClass: function(name, when){ - return this.each(function(idx){ - var $this = $(this), names = funcArg(this, name, idx, className(this)) - names.split(/\s+/g).forEach(function(klass){ - (when === undefined ? !$this.hasClass(klass) : when) ? - $this.addClass(klass) : $this.removeClass(klass) - }) - }) - }, - scrollTop: function(){ - if (!this.length) return - return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY - }, - position: function() { - if (!this.length) return - - var elem = this[0], - // Get *real* offsetParent - offsetParent = this.offsetParent(), - // Get correct offsets - offset = this.offset(), - parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() - - // Subtract element margins - // note: when an element has margin: auto the offsetLeft and marginLeft - // are the same in Safari causing offset.left to incorrectly be 0 - offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 - offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 - - // Add offsetParent borders - parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 - parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 - - // Subtract the two offsets - return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left - } - }, - offsetParent: function() { - return this.map(function(){ - var parent = this.offsetParent || document.body - while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") - parent = parent.offsetParent - return parent - }) - } - } - - // for now - $.fn.detach = $.fn.remove - - // Generate the `width` and `height` functions - ;['width', 'height'].forEach(function(dimension){ - $.fn[dimension] = function(value){ - var offset, el = this[0], - Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() }) - if (value === undefined) return isWindow(el) ? el['inner' + Dimension] : - isDocument(el) ? el.documentElement['offset' + Dimension] : - (offset = this.offset()) && offset[dimension] - else return this.each(function(idx){ - el = $(this) - el.css(dimension, funcArg(this, value, idx, el[dimension]())) - }) - } - }) - - function traverseNode(node, fun) { - fun(node) - for (var key in node.childNodes) traverseNode(node.childNodes[key], fun) - } - - // Generate the `after`, `prepend`, `before`, `append`, - // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. - adjacencyOperators.forEach(function(operator, operatorIndex) { - var inside = operatorIndex % 2 //=> prepend, append - - $.fn[operator] = function(){ - // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings - var argType, nodes = $.map(arguments, function(arg) { - argType = type(arg) - return argType == "object" || argType == "array" || arg == null ? - arg : zepto.fragment(arg) - }), - parent, copyByClone = this.length > 1 - if (nodes.length < 1) return this - - return this.each(function(_, target){ - parent = inside ? target : target.parentNode - - // convert all methods to a "before" operation - target = operatorIndex == 0 ? target.nextSibling : - operatorIndex == 1 ? target.firstChild : - operatorIndex == 2 ? target : - null - - nodes.forEach(function(node){ - if (copyByClone) node = node.cloneNode(true) - else if (!parent) return $(node).remove() - - traverseNode(parent.insertBefore(node, target), function(el){ - if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && - (!el.type || el.type === 'text/javascript') && !el.src) - window['eval'].call(window, el.innerHTML) - }) - }) - }) - } - - // after => insertAfter - // prepend => prependTo - // before => insertBefore - // append => appendTo - $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ - $(html)[operator](this) - return this - } - }) - - zepto.Z.prototype = $.fn - - // Export internal API functions in the `$.zepto` namespace - zepto.uniq = uniq - zepto.deserializeValue = deserializeValue - $.zepto = zepto - - return $ -})() - -window.Zepto = Zepto -'$' in window || (window.$ = Zepto) - -;(function($){ - var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={}, - hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } - - specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' - - function zid(element) { - return element._zid || (element._zid = _zid++) - } - function findHandlers(element, event, fn, selector) { - event = parse(event) - if (event.ns) var matcher = matcherFor(event.ns) - return (handlers[zid(element)] || []).filter(function(handler) { - return handler - && (!event.e || handler.e == event.e) - && (!event.ns || matcher.test(handler.ns)) - && (!fn || zid(handler.fn) === zid(fn)) - && (!selector || handler.sel == selector) - }) - } - function parse(event) { - var parts = ('' + event).split('.') - return {e: parts[0], ns: parts.slice(1).sort().join(' ')} - } - function matcherFor(ns) { - return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') - } - - function eachEvent(events, fn, iterator){ - if ($.type(events) != "string") $.each(events, iterator) - else events.split(/\s/).forEach(function(type){ iterator(type, fn) }) - } - - function eventCapture(handler, captureSetting) { - return handler.del && - (handler.e == 'focus' || handler.e == 'blur') || - !!captureSetting - } - - function realEvent(type) { - return hover[type] || type - } - - function add(element, events, fn, selector, getDelegate, capture){ - var id = zid(element), set = (handlers[id] || (handlers[id] = [])) - eachEvent(events, fn, function(event, fn){ - var handler = parse(event) - handler.fn = fn - handler.sel = selector - // emulate mouseenter, mouseleave - if (handler.e in hover) fn = function(e){ - var related = e.relatedTarget - if (!related || (related !== this && !$.contains(this, related))) - return handler.fn.apply(this, arguments) - } - handler.del = getDelegate && getDelegate(fn, event) - var callback = handler.del || fn - handler.proxy = function (e) { - var result = callback.apply(element, [e].concat(e.data)) - if (result === false) e.preventDefault(), e.stopPropagation() - return result - } - handler.i = set.length - set.push(handler) - element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) - }) - } - function remove(element, events, fn, selector, capture){ - var id = zid(element) - eachEvent(events || '', fn, function(event, fn){ - findHandlers(element, event, fn, selector).forEach(function(handler){ - delete handlers[id][handler.i] - element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) - }) - }) - } - - $.event = { add: add, remove: remove } - - $.proxy = function(fn, context) { - if ($.isFunction(fn)) { - var proxyFn = function(){ return fn.apply(context, arguments) } - proxyFn._zid = zid(fn) - return proxyFn - } else if (typeof context == 'string') { - return $.proxy(fn[context], fn) - } else { - throw new TypeError("expected function") - } - } - - $.fn.bind = function(event, callback){ - return this.each(function(){ - add(this, event, callback) - }) - } - $.fn.unbind = function(event, callback){ - return this.each(function(){ - remove(this, event, callback) - }) - } - $.fn.one = function(event, callback){ - return this.each(function(i, element){ - add(this, event, callback, null, function(fn, type){ - return function(){ - var result = fn.apply(element, arguments) - remove(element, type, fn) - return result - } - }) - }) - } - - var returnTrue = function(){return true}, - returnFalse = function(){return false}, - ignoreProperties = /^([A-Z]|layer[XY]$)/, - eventMethods = { - preventDefault: 'isDefaultPrevented', - stopImmediatePropagation: 'isImmediatePropagationStopped', - stopPropagation: 'isPropagationStopped' - } - function createProxy(event) { - var key, proxy = { originalEvent: event } - for (key in event) - if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] - - $.each(eventMethods, function(name, predicate) { - proxy[name] = function(){ - this[predicate] = returnTrue - return event[name].apply(event, arguments) - } - proxy[predicate] = returnFalse - }) - return proxy - } - - // emulates the 'defaultPrevented' property for browsers that have none - function fix(event) { - if (!('defaultPrevented' in event)) { - event.defaultPrevented = false - var prevent = event.preventDefault - event.preventDefault = function() { - this.defaultPrevented = true - prevent.call(this) - } - } - } - - $.fn.delegate = function(selector, event, callback){ - return this.each(function(i, element){ - add(element, event, callback, selector, function(fn){ - return function(e){ - var evt, match = $(e.target).closest(selector, element).get(0) - if (match) { - evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) - return fn.apply(match, [evt].concat([].slice.call(arguments, 1))) - } - } - }) - }) - } - $.fn.undelegate = function(selector, event, callback){ - return this.each(function(){ - remove(this, event, callback, selector) - }) - } - - $.fn.live = function(event, callback){ - $(document.body).delegate(this.selector, event, callback) - return this - } - $.fn.die = function(event, callback){ - $(document.body).undelegate(this.selector, event, callback) - return this - } - - $.fn.on = function(event, selector, callback){ - return !selector || $.isFunction(selector) ? - this.bind(event, selector || callback) : this.delegate(selector, event, callback) - } - $.fn.off = function(event, selector, callback){ - return !selector || $.isFunction(selector) ? - this.unbind(event, selector || callback) : this.undelegate(selector, event, callback) - } - - $.fn.trigger = function(event, data){ - if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event) - fix(event) - event.data = data - return this.each(function(){ - // items in the collection might not be DOM elements - // (todo: possibly support events on plain old objects) - if('dispatchEvent' in this) this.dispatchEvent(event) - }) - } - - // triggers event handlers on current element just as if an event occurred, - // doesn't trigger an actual event, doesn't bubble - $.fn.triggerHandler = function(event, data){ - var e, result - this.each(function(i, element){ - e = createProxy(typeof event == 'string' ? $.Event(event) : event) - e.data = data - e.target = element - $.each(findHandlers(element, event.type || event), function(i, handler){ - result = handler.proxy(e) - if (e.isImmediatePropagationStopped()) return false - }) - }) - return result - } - - // shortcut methods for `.bind(event, fn)` for each event type - ;('focusin focusout load resize scroll unload click dblclick '+ - 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ - 'change select keydown keypress keyup error').split(' ').forEach(function(event) { - $.fn[event] = function(callback) { - return callback ? - this.bind(event, callback) : - this.trigger(event) - } - }) - - ;['focus', 'blur'].forEach(function(name) { - $.fn[name] = function(callback) { - if (callback) this.bind(name, callback) - else this.each(function(){ - try { this[name]() } - catch(e) {} - }) - return this - } - }) - - $.Event = function(type, props) { - if (typeof type != 'string') props = type, type = props.type - var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true - if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) - event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null) - event.isDefaultPrevented = function(){ return this.defaultPrevented } - return event - } - -})(Zepto) |
