From 11bd4396b922bfd34cdf083f70cfd14dea69592f Mon Sep 17 00:00:00 2001 From: Casey Foster Date: Tue, 4 Sep 2012 12:07:36 -0700 Subject: [PATCH] Initial commit --- .gitignore | 2 + LICENSE | 19 + Makefile | 35 ++ README.md | 49 ++ dist/backbone-link.js | 76 +++ dist/cookie.js | 62 +++ dist/jquery-cwd.js | 1043 ++++++++++++++++++++++++++++++++++++++ dist/konami.js | 77 +++ dist/lazy.js | 53 ++ dist/placeholder.js | 105 ++++ dist/pop-up.js | 138 +++++ dist/scroll.js | 68 +++ dist/search.js | 233 +++++++++ dist/tooltip.js | 268 ++++++++++ lib/backbone-link.coffee | 51 ++ lib/cookie.coffee | 33 ++ lib/konami.coffee | 53 ++ lib/lazy.coffee | 35 ++ lib/placeholder.coffee | 62 +++ lib/pop-up.coffee | 121 +++++ lib/scroll.coffee | 40 ++ lib/search.coffee | 146 ++++++ lib/tooltip.coffee | 229 +++++++++ package.json | 19 + test/test.coffee | 6 + test/test.html | 19 + test/test.js | 11 + 27 files changed, 3053 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 dist/backbone-link.js create mode 100644 dist/cookie.js create mode 100644 dist/jquery-cwd.js create mode 100644 dist/konami.js create mode 100644 dist/lazy.js create mode 100644 dist/placeholder.js create mode 100644 dist/pop-up.js create mode 100644 dist/scroll.js create mode 100644 dist/search.js create mode 100644 dist/tooltip.js create mode 100644 lib/backbone-link.coffee create mode 100644 lib/cookie.coffee create mode 100644 lib/konami.coffee create mode 100644 lib/lazy.coffee create mode 100644 lib/placeholder.coffee create mode 100644 lib/pop-up.coffee create mode 100644 lib/scroll.coffee create mode 100644 lib/search.coffee create mode 100644 lib/tooltip.coffee create mode 100644 package.json create mode 100644 test/test.coffee create mode 100644 test/test.html create mode 100644 test/test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03e05e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +/node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b84cff7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 Casey Foster + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..902908b --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +BIN=node_modules/.bin/ +COFFEE=$(BIN)coffee + +all: npm-install compile-lib compile-test + +npm-install: + npm install + +compile-join: + $(COFFEE) -c -j dist/jquery-cwd.js lib + +compile-join-w: + $(COFFEE) -cw -j dist/jquery-cwd.js lib + +compile-lib: + $(COFFEE) -c -o dist lib + +compile-lib-w: + $(COFFEE) -cw -o dist lib + +compile-test: + $(COFFEE) -c -j test/test.js test + +compile-test-w: + $(COFFEE) -cw -j test/test.js test + +dev: + make -j dev-j + +dev-j: compile-join-w compile-lib-w compile-test-w + +test: all + open test/test.html + +.PHONY: test diff --git a/README.md b/README.md new file mode 100644 index 0000000..07502ac --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +jquery-cwd +========== + +jQuery plugins by caseyWebDev. + +Install +------- + +```html + +``` + +or download `jquery-cwd.js` and reference your own copy. You'll also need to have [jQuery](https://github.com/jquery/jquery) and [Underscore](https://github.com/documentcloud/underscore) on the page before loading these extensions. + +Use +--- + +TODO + +Test +---- + +```bash +make test +``` + +Licence +------- + +Copyright (C) 2012 Casey Foster + +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. + diff --git a/dist/backbone-link.js b/dist/backbone-link.js new file mode 100644 index 0000000..748f41b --- /dev/null +++ b/dist/backbone-link.js @@ -0,0 +1,76 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var backboneLink, backboneUnlink; + + $.extend($.fn, backboneLink = function(options) { + var attr, model, save; + if (options == null) { + options = {}; + } + model = options.model, attr = options.attr, save = options.save; + if (save == null) { + save = true; + } + return $(this).each(function() { + var check; + check = $(this).find('[data-backbone-link-attr]'); + return (check.length ? check : $(this)).each(function() { + var $t, checkbox; + $t = $(this); + $t.backboneUnlink(); + $t.data({ + backboneLinkModel: model + }); + checkbox = $t.is(':checkbox'); + $t.data({ + backboneLinkInputChange: function() { + var newVal, oldVal; + oldVal = model.get(attr); + newVal = checkbox ? $t.is(':checked') : $t.val(); + if (attr === 'id' || _.endsWith(attr, '_id') || _.endsWith(attr, 'Id')) { + newVal = newVal ? parseInt(newVal) : null; + } + model.set(attr, newVal === '' ? null : newVal); + if (save && newVal !== oldVal) { + return model.save(); + } + } + }); + $t.data({ + backboneLinkModelChange: function() { + var newVal, oldVal, valOrText; + valOrText = $t.is(':input') ? 'val' : 'text'; + oldVal = checkbox ? $t.is(':checked') : $t[valOrText](); + newVal = model.get(attr); + newVal = newVal === null ? '' : newVal; + if (newVal !== oldVal) { + if (checkbox) { + return $t.prop({ + checked: !!newVal + }); + } else { + return $t[valOrText](newVal); + } + } + } + }); + if ($t.is(':input')) { + $t.on('change', $t.data().backboneLinkInputChange); + } + model.on("change:" + attr, $t.data().backboneLinkModelChange); + return $t.data().backboneLinkModelChange(); + }); + }); + }, backboneUnlink = function() { + return $(this).each(function() { + var $t, model; + $t = $(this); + model = $t.data().backboneLinkModel; + if (model) { + $t.off('change', $t.data().backboneLinkInputChange); + return model.off(null, $t.data().backboneLinkModelChange); + } + }); + }); + +}).call(this); diff --git a/dist/cookie.js b/dist/cookie.js new file mode 100644 index 0000000..1740149 --- /dev/null +++ b/dist/cookie.js @@ -0,0 +1,62 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + + $.Cookie = function(name, val, options) { + var cookie, cookies, encodeName, encodeVal, n, params, rawCookies, v, _i, _len, _ref; + if (options == null) { + options = {}; + } + if (typeof name === 'object') { + for (n in name) { + v = name[n]; + $.Cookie(n, v, val); + } + return $.Cookie(); + } else if (typeof name === 'string' && val !== void 0) { + if (val === null) { + options.expires = -1; + } + val || (val = ''); + params = []; + if (options.expires) { + params.push("; Expires=" + (options.expires.toGMTString != null ? options.expires.toGMTString() : new Date(+(new Date) + options.expires).toGMTString())); + } + if (options.path) { + params.push("; Path=" + options.path); + } + if (options.domain) { + params.push("; Domain=" + options.domain); + } + if (options.httpOnly) { + params.push('; HttpOnly'); + } + if (options.secure) { + params.push('; Secure'); + } + encodeName = encodeURIComponent(name); + encodeVal = encodeURIComponent(val); + document.cookie = "" + encodeName + "=" + encodeVal + (params.join('')); + return $.Cookie(); + } else { + cookies = {}; + if (document.cookie) { + rawCookies = decodeURIComponent(document.cookie).split(/\s*;\s*/); + for (_i = 0, _len = rawCookies.length; _i < _len; _i++) { + cookie = rawCookies[_i]; + _ref = /^([^=]*)\s*=\s*(.*)$/.exec(cookie), n = _ref[1], v = _ref[2]; + if (typeof name === 'string' && name === n) { + return v; + } else if (!name) { + cookies[n] = v; + } + } + } + if (!name) { + return cookies; + } else { + return null; + } + } + }; + +}).call(this); diff --git a/dist/jquery-cwd.js b/dist/jquery-cwd.js new file mode 100644 index 0000000..d90892a --- /dev/null +++ b/dist/jquery-cwd.js @@ -0,0 +1,1043 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var Lazy, Placeholder, PopUp, Search, Tooltip, backboneLink, backboneUnlink, + _this = this; + + $.extend($.fn, backboneLink = function(options) { + var attr, model, save; + if (options == null) { + options = {}; + } + model = options.model, attr = options.attr, save = options.save; + if (save == null) { + save = true; + } + return $(this).each(function() { + var check; + check = $(this).find('[data-backbone-link-attr]'); + return (check.length ? check : $(this)).each(function() { + var $t, checkbox; + $t = $(this); + $t.backboneUnlink(); + $t.data({ + backboneLinkModel: model + }); + checkbox = $t.is(':checkbox'); + $t.data({ + backboneLinkInputChange: function() { + var newVal, oldVal; + oldVal = model.get(attr); + newVal = checkbox ? $t.is(':checked') : $t.val(); + if (attr === 'id' || _.endsWith(attr, '_id') || _.endsWith(attr, 'Id')) { + newVal = newVal ? parseInt(newVal) : null; + } + model.set(attr, newVal === '' ? null : newVal); + if (save && newVal !== oldVal) { + return model.save(); + } + } + }); + $t.data({ + backboneLinkModelChange: function() { + var newVal, oldVal, valOrText; + valOrText = $t.is(':input') ? 'val' : 'text'; + oldVal = checkbox ? $t.is(':checked') : $t[valOrText](); + newVal = model.get(attr); + newVal = newVal === null ? '' : newVal; + if (newVal !== oldVal) { + if (checkbox) { + return $t.prop({ + checked: !!newVal + }); + } else { + return $t[valOrText](newVal); + } + } + } + }); + if ($t.is(':input')) { + $t.on('change', $t.data().backboneLinkInputChange); + } + model.on("change:" + attr, $t.data().backboneLinkModelChange); + return $t.data().backboneLinkModelChange(); + }); + }); + }, backboneUnlink = function() { + return $(this).each(function() { + var $t, model; + $t = $(this); + model = $t.data().backboneLinkModel; + if (model) { + $t.off('change', $t.data().backboneLinkInputChange); + return model.off(null, $t.data().backboneLinkModelChange); + } + }); + }); + + $.Cookie = function(name, val, options) { + var cookie, cookies, encodeName, encodeVal, n, params, rawCookies, v, _i, _len, _ref; + if (options == null) { + options = {}; + } + if (typeof name === 'object') { + for (n in name) { + v = name[n]; + $.Cookie(n, v, val); + } + return $.Cookie(); + } else if (typeof name === 'string' && val !== void 0) { + if (val === null) { + options.expires = -1; + } + val || (val = ''); + params = []; + if (options.expires) { + params.push("; Expires=" + (options.expires.toGMTString != null ? options.expires.toGMTString() : new Date(+(new Date) + options.expires).toGMTString())); + } + if (options.path) { + params.push("; Path=" + options.path); + } + if (options.domain) { + params.push("; Domain=" + options.domain); + } + if (options.httpOnly) { + params.push('; HttpOnly'); + } + if (options.secure) { + params.push('; Secure'); + } + encodeName = encodeURIComponent(name); + encodeVal = encodeURIComponent(val); + document.cookie = "" + encodeName + "=" + encodeVal + (params.join('')); + return $.Cookie(); + } else { + cookies = {}; + if (document.cookie) { + rawCookies = decodeURIComponent(document.cookie).split(/\s*;\s*/); + for (_i = 0, _len = rawCookies.length; _i < _len; _i++) { + cookie = rawCookies[_i]; + _ref = /^([^=]*)\s*=\s*(.*)$/.exec(cookie), n = _ref[1], v = _ref[2]; + if (typeof name === 'string' && name === n) { + return v; + } else if (!name) { + cookies[n] = v; + } + } + } + if (!name) { + return cookies; + } else { + return null; + } + } + }; + + $.Konami = function(callback, options) { + var checkEvents, dX, dY, keyDownEvent, keysPressed, startX, startY, tap, touchEndEvent, touchEvents, touchMoveEvent, touchStartEvent; + if (options == null) { + options = {}; + } + options = _.extend({ + onlyOnce: false, + code: '38,38,40,40,37,39,37,39,66,65,13', + touchCode: 'up,up,down,down,left,right,left,right,tap,tap,tap' + }, options); + keysPressed = []; + touchEvents = []; + tap = false; + startX = startY = dX = dY = 0; + keyDownEvent = function(e) { + keysPressed.push(e.keyCode); + if (_.endsWith(keysPressed + '', options.code)) { + if (options.onlyOnce) { + $(document).off('keydown', keyDownEvent); + } + keysPressed = []; + e.preventDefault(); + return callback(); + } + }; + touchStartEvent = function(e) { + var touch, tracking; + e = e.originalEvent; + if (e.touches.length === 1) { + touch = e.touches[0]; + startX = touch.screenX, startY = touch.screenY; + return tap = tracking = true; + } + }; + touchMoveEvent = function(e) { + var downUp, rightLeft, touch, val; + e = e.originalEvent; + if (e.touches.length === 1 && tap) { + touch = e.touches[0]; + dX = touch.screenX - startX; + dY = touch.screenY - startY; + rightLeft = dX > 0 ? 'right' : 'left'; + downUp = dY > 0 ? 'down' : 'up'; + val = Math.abs(dX) > Math.abs(dY) ? rightLeft : downUp; + touchEvents.push(val); + tap = false; + return checkEvents(e); + } + }; + touchEndEvent = function(e) { + e = e.originalEvent; + if (e.touches.length === 0 && tap) { + touchEvents.push('tap'); + return checkEvents(e); + } + }; + checkEvents = function(e) { + if (_.endsWith(touchEvents + '', options.touchCode)) { + if (options.onlyOnce) { + $(document).off('touchmove', touchMoveEvent); + $(document).off('touchend', touchEndEvent); + } + touchEvents = []; + e.preventDefault(); + return callback(); + } + }; + $(document).on('keydown', keyDownEvent); + $(document).on('touchstart', touchStartEvent); + $(document).on('touchmove', touchMoveEvent); + return $(document).on('touchend', touchEndEvent); + }; + + Lazy = { + $els: $(), + bind: function() { + return $(window).on('scroll resize', function() { + return Lazy.check(Lazy.$els); + }); + }, + check: function($imgs) { + $imgs.each(function() { + var $t; + $t = $(this); + return _.defer(function() { + var fold, showLine, visible; + visible = _.reduce($t.parents(), function(memo, parent) { + return memo && $(parent).css('display') !== 'none' && $(parent).css('visibility') !== 'hidden'; + }, true); + fold = $(window).scrollTop() + $(window).outerHeight(); + showLine = $t.offset().top - $t.data().lazyTolerance; + if (visible && fold >= showLine) { + $t.attr('src', $t.data().lazySrc); + return Lazy.$els = Lazy.$els.not($t); + } + }); + }); + return $imgs; + } + }; + + Lazy.init = _.once(Lazy.bind); + + $.fn.lazy = function(src, options) { + var $t, tolerance; + if (options == null) { + options = {}; + } + tolerance = options.tolerance; + if (tolerance == null) { + tolerance = 100; + } + $t = $(this); + Lazy.init(); + Lazy.$els = Lazy.$els.add($t.data({ + lazySrc: src, + lazyTolerance: tolerance + })); + return Lazy.check($t); + }; + + Placeholder = { + bind: function() { + var val; + val = $.fn.val; + return $.fn.val = function(str) { + var $t; + $t = $(this); + if (str === void 0) { + if ($t.data().placeholderIsEmpty) { + return ''; + } else { + return val.call($t); + } + } else { + if (($t.data().placeholderIsEmpty != null) && !$t.is(':focus')) { + if (str === '' || str === null) { + $t.data({ + placeholderIsEmpty: true + }); + val.call($t, $t.data().placeholderText); + if ($t.data().placeholderIsPassword) { + $t[0].type = 'text'; + } + } else { + val.call($t, str); + if ($t.data().placeholderIsPassword) { + $t[0].type = 'password'; + } + $t.data({ + placeholderIsEmpty: false + }); + } + } else { + val.call($t, str); + } + return $t; + } + }; + }, + listeners: { + focus: function() { + var $t; + $t = $(this); + return _.defer(function() { + if ($t.data().placeholderIsPassword) { + $t[0].type = 'password'; + } + if ($t.data().placeholderIsEmpty) { + $t.val(''); + } + return $t.data({ + placeholderIsEmpty: false + }); + }); + }, + blur: function() { + var $t; + $t = $(this); + return _.defer(function() { + if (!$t.val()) { + return $t.val(''); + } + }); + } + } + }; + + Placeholder.init = _.once(Placeholder.bind); + + $.fn.placeholder = function(text, options) { + var password; + if (options == null) { + options = {}; + } + password = options.password; + Placeholder.init(); + return $(this).each(function() { + var $t; + if (($t = $(this)).data().placeholderIsEmpty == null) { + if (password) { + $t[0].type = 'password'; + } + if (!(password && $.browser.msie && $.browser.version.split('.')[0] < 9)) { + $t.data({ + placeholderText: text, + placeholderIsEmpty: false, + placeholderIsPassword: text + }); + if (!$t.val() || $t.val() === text) { + $t.val(''); + } + return $t.attr({ + placeholder: text, + title: text + }).on(Placeholder.listeners); + } + } + }); + }; + + PopUp = { + bind: function() { + $('body').append(PopUp.$container = $('
').attr({ + id: 'js-pop-up-container' + }).css({ + display: 'none', + position: 'fixed', + zIndex: 999999, + left: 0, + top: 0, + width: '100%', + height: '100%', + opacity: 0, + overflow: 'auto' + }).append($('
').attr({ + id: 'js-pop-up-table' + }).css({ + display: 'table', + width: '100%', + height: '100%' + }).append($('
').attr({ + id: 'js-pop-up-table-cell' + }).css({ + display: 'table-cell', + textAlign: 'center', + verticalAlign: 'middle' + }).append(PopUp.$div = $('
').attr({ + id: 'js-pop-up' + }).css({ + display: 'inline-block', + position: 'relative' + }))))); + $.PopUp.hide(); + PopUp.$container.on('click', function() { + return PopUp.$div.find('.js-pop-up-outside').click(); + }); + PopUp.$div.on('click', function(e) { + return e.stopPropagation(); + }).on('click', '.js-pop-up-hide', function() { + return $.PopUp.hide; + }); + return $(document).keydown(function(e) { + if (PopUp.$container.css('display') === 'block' && !$('body :focus').length) { + switch (e.keyCode) { + case 13: + PopUp.$div.find('.js-pop-up-enter').click(); + break; + case 27: + PopUp.$div.find('.js-pop-up-esc').click(); + break; + default: + return true; + } + return false; + } + }); + } + }; + + PopUp.init = _.once(PopUp.bind); + + $.PopUp = { + duration: 0, + fadeDuration: 250, + show: function(el, options) { + var $body; + if (options == null) { + options = {}; + } + PopUp.init(); + options = _.extend({ + duration: $.PopUp.duration, + callback: null, + fadeDuration: $.PopUp.fadeDuration + }, options); + $body = $('body'); + if (PopUp.$container.css('display') !== 'block') { + PopUp.bodyStyle = $body.attr('style'); + } + if ($('body').hasScrollbar()) { + $body.css({ + marginRight: $.scrollbarSize() + }); + } + $body.css({ + overflow: 'hidden' + }).find(':focus').blur(); + PopUp.fadeDuration = options.fadeDuration; + PopUp.$div.empty(); + if (typeof el === 'string') { + PopUp.$div.html(el); + } else { + PopUp.$div.append(el); + } + PopUp.$container.stop().css({ + display: 'block' + }).animate({ + opacity: 1 + }, options.fadeDuration); + clearTimeout(PopUp.timeout); + if (options.duration) { + return PopUp.timeout = _.delay(function() { + $.PopUp.hide(); + return typeof options.callback === "function" ? options.callback() : void 0; + }, options.duration); + } + }, + hide: function(fadeDuration) { + if (fadeDuration == null) { + fadeDuration = PopUp.fadeDuration; + } + if (!PopUp.$container) { + return; + } + return PopUp.$container.stop().animate({ + opacity: 0 + }, fadeDuration, function() { + PopUp.$container.css({ + display: 'none' + }); + if (PopUp.bodyStyle) { + $('body').attr({ + style: PopUp.saveBodyStyle + }); + } else { + $('body').removeAttr('style'); + } + return delete PopUp.bodyStyle; + }); + } + }; + + $.fn.scrollTo = function(val, options) { + if (val == null) { + val = 0; + } + if (options == null) { + options = {}; + } + if (val instanceof $) { + val = val.offset().top; + } + return $(this).animate({ + scrollTop: val + }, options); + }; + + $.scrollTo = function() { + var $el; + $el = $($.browser.webkit ? document.body : document.documentElement); + return $el.scrollTo.apply($el, arguments); + }; + + $.scrollbarSize = function(dimension) { + var $in, $out, d1, d2; + if (dimension == null) { + dimension = 'width'; + } + $out = $('
').appendTo('body').css({ + position: 'fixed', + overflow: 'hidden', + left: -50, + top: -50, + width: 50, + height: 50 + }); + $in = $out.find('> div').css({ + height: '100%' + }); + d1 = $in[dimension](); + $out.css({ + overflow: 'scroll' + }); + d2 = $in[dimension](); + $out.remove(); + return d1 - d2; + }; + + $.fn.hasScrollbar = function() { + var $t, d1, d2, style; + $t = $(this); + style = $t.attr('style'); + d1 = $t.width(); + d2 = $t.css({ + overflow: 'hidden' + }).width(); + if (style != null) { + $t.attr({ + style: style + }); + } else { + $t.removeAttr('style'); + } + return d1 !== d2; + }; + + Search = { + clean: function(str) { + return str.toLowerCase().replace(/\s{2,}/g, ' ').replace(/^\s*|\s*$/g, ''); + }, + page: function($searches, n, prev) { + return $searches.each(function() { + var $results, $search; + $search = $(this); + $results = $search.data('search$Results'); + n = Math.min($results.find('.page').length - 1, Math.max(n, 0)); + $results.find('.selected').removeClass('selected'); + $results.find('.page').css({ + display: 'none' + }).eq(n).removeAttr('style').find('.result:not(.prev):not(.next)')[prev ? 'last' : 'first']().addClass('selected'); + return $search.data('searchPage', n); + }); + }, + query: function($searches, urlN) { + if (urlN == null) { + urlN = 1; + } + return $searches.each(function() { + var $q, $results, $search, anotherSearch, callback, o, q, t, _base, _ref; + o = Search; + $search = $(this); + $results = $search.data('search$Results'); + $q = $search.data('search$Q'); + callback = $search.data().searchCallback; + q = Search.clean($q.val()); + t = new Date().getTime(); + $results.css({ + display: 'block' + }); + anotherSearch = $search.data("searchUrl" + (urlN + 1)) != null; + if (!(q || ($search.data('empty') != null))) { + $results.css({ + display: 'none' + }).empty(); + $search.removeClass('loading'); + } else if (q !== $search.data().searchLastQ || urlN > 1) { + $search.addClass('loading'); + callback($search, null, urlN); + clearTimeout($search.data().searchTimeout); + if (typeof (_base = $search.data().searchAjax).abort === "function") { + _base.abort(); + } + if ($search.data().searchCache[("" + urlN + "_") + q] != null) { + if (!anotherSearch) { + $search.removeClass('loading'); + } + callback($search, $search.data().searchCache[("" + urlN + "_") + q], urlN); + if (anotherSearch) { + o.query($search, urlN + 1); + } + } else { + $search.data('searchTimeout', setTimeout(function() { + var check, handleData; + handleData = function(data) { + $search.data().searchCache[("" + urlN + "_") + q] = data; + if (check === $search.data().searchId && (_.clean($q.val()) || ($search.data('empty') != null))) { + if (!anotherSearch) { + $search.removeClass('loading'); + } + callback($search, data, urlN); + } + if (anotherSearch) { + return o.query($search, urlN + 1); + } + }; + check = $search.data({ + searchId: $search.data().searchId + 1 + }).data().searchId; + if ($search.data().searchJs) { + return handleData($search.data().searchJs(q)); + } else if ($search.data().searchUrl != null) { + return $search.data({ + searchAjax: $.getJSON($search.data("searchUrl" + (urlN === 1 ? '' : urlN)), { + q: q + }, handleData) + }); + } + }, (_ref = $search.data().searchDelay) != null ? _ref : 0)); + } + } + return $search.data('searchLastQ', q); + }); + }, + select: function($searches, dir) { + return $searches.each(function() { + var $page, $search, o; + o = Search; + $search = $(this); + $page = $search.find('.page').eq($search.data().searchPage); + if (!$page.find('.selected').removeClass('selected')[dir]().addClass('selected').length) { + $page.find('.result')[dir === 'prev' ? 'first' : 'last']().addClass('selected'); + } + if ($page.find('.result.selected.prev').length) { + return o.page($search, $search.data().searchPage - 1, true); + } else if ($page.find('.result.selected.next').length) { + return o.page($search, $search.data().searchPage + 1); + } + }); + } + }; + + $.fn.search = function($q, $results, options) { + var $search, key, o, val; + if (options == null) { + options = {}; + } + $search = $(this); + if ($search.data().searchCache == null) { + o = Search; + $search.data({ + searchCache: [], + searchId: 0, + searchAjax: {}, + searchLastQ: null, + searchPage: 0, + searchHoldHover: false, + search$Q: $q, + search$Results: $results + }); + for (key in options) { + val = options[key]; + $search.data("search" + (key.replace(/(\w)/, function(s) { + return s.toUpperCase(); + })), val); + } + if ($q.is(':focus')) { + o.query($search); + } + $search.hover(function() { + return $search.data({ + searchHover: true + }); + }, function() { + $search.data({ + searchHover: false + }); + if (!($q.is(':focus') || $search.data().searchHoldHover)) { + return $results.css({ + display: 'none' + }); + } + }).mouseover(function() { + return $search.data({ + searchHoldHover: false + }); + }); + $q.blur(function() { + return _.defer(function() { + if (!$search.data().searchHover) { + return $results.css({ + display: 'none' + }); + } + }); + }).focus(function() { + return $search.data({ + searchHoldHover: false + }); + }).keydown(function(e) { + switch (e.keyCode) { + case 13: + $search.find('.selected').click(); + break; + case 38: + o.select($search, 'prev'); + break; + case 40: + o.select($search, 'next'); + break; + case 27: + if ($q.val() === '') { + $q.blur(); + _.defer(function() { + return o.query($search); + }); + } else { + _.defer(function() { + $q.val(''); + return o.query($search); + }); + } + break; + default: + _.defer(function() { + if ($q.is(':focus')) { + return o.query($search); + } + }); + return true; + } + return false; + }).on('focus keyup change', function() { + return o.query($search); + }); + return $results.on('mouseenter click', '.result', function(e) { + var $t; + $t = $(this); + $results.find('.result.selected').removeClass('selected'); + $t.addClass('selected'); + if (e.type === 'click') { + if ($t.hasClass('prev')) { + o.page($search, $search.data().searchPage - 1, true); + $search.data({ + searchHoldHover: true + }); + } else if ($t.hasClass('next')) { + o.page($search, $search.data().searchPage + 1); + $search.data({ + searchHoldHover: true + }); + } else if ($t.hasClass('submit')) { + $t.parents('form').submit(); + } + if ($t.hasClass('hide')) { + $q.blur(); + return $results.css({ + display: 'none' + }); + } + } + }); + } + }; + + Tooltip = { + $els: $(), + mouse: { + x: 0, + y: 0 + }, + bind: function() { + return $('body').mousemove(function(e) { + return Tooltip.mouse = { + x: e.pageX, + y: e.pageY + }; + }); + }, + listeners: { + mousemove: function() { + var $t; + if (($t = $(this)).data('tooltip$Div')) { + return $t.data().tooltip$Div.css(Tooltip.position($t).home); + } + }, + mouseenter: function() { + var $t; + Tooltip.show($t = $(this)); + return $t.data({ + tooltipHover: true + }); + }, + mouseleave: function() { + var $t; + ($t = $(this)).data({ + tooltipHover: false + }); + return Tooltip.hide($t); + }, + focus: function() { + return Tooltip.show($(this)); + }, + blur: function() { + return Tooltip.hide($(this)); + } + }, + add: function($els, options) { + var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; + if (options == null) { + options = {}; + } + Tooltip.remove($els); + Tooltip.$els = Tooltip.$els.add($els); + $els.data({ + tooltipHtml: typeof html !== "undefined" && html !== null ? html : '', + tooltipPosition: (_ref = options.position) != null ? _ref : 'top', + tooltipOffset: (_ref1 = options.offset) != null ? _ref1 : 0, + tooltipDuration: (_ref2 = options.duration) != null ? _ref2 : 0, + tooltipNoHover: (_ref3 = options.noHover) != null ? _ref3 : false, + tooltipNoFocus: (_ref4 = options.noFocus) != null ? _ref4 : false, + tooltipMouse: (_ref5 = options.mouse) != null ? _ref5 : false, + tooltipHoverableHover: (_ref6 = options.hoverableHover) != null ? _ref6 : false + }); + if (options.mouse) { + $els.on('mousemove', Tooltip.listeners.mousemove); + } + if (!options.noHover) { + $els.on('mouseenter', Tooltip.listeners.mouseenter); + $els.on('mouseleave', Tooltip.listeners.mouseleave); + } + if (!options.noFocus) { + $els.on('focus', Tooltip.listeners.focus); + $els.on('blur', Tooltip.listeners.blur); + } + return $els; + }, + remove: function($els) { + Tooltip.$els = Tooltip.$els.no($els); + return $els.data({ + tooltipHover: false, + tooltipHoverableHover: false + }).off(Tooltip.listeners).each(function() { + var _ref; + return (_ref = $(this).data().tooltip$Div) != null ? _ref.remove() : void 0; + }); + }, + divFor: function($t) { + var $div, position; + if (!$t.data('tooltip$Div')) { + if ($t.parent().css({ + position: 'static' + })) { + $t.parent().css({ + position: 'relative' + }); + } + if ($t.data().tooltipMouse != null) { + $t.data({ + tooltipHoverable: false + }); + } + $t.data(_.extend({ + tooltipPosition: 'top', + tooltipOffset: 0, + tooltipDuration: 0 + }, $t.data())); + $div = $('
').addClass("tooltip " + ($t.data().tooltipPosition)).css({ + display: 'none', + position: 'absolute', + zIndex: 999999 + }).append($('
').html($t.data().tooltipHtml).css({ + position: 'relative' + })); + $t.data({ + tooltip$Div: $div + }).parent().append($div); + position = Tooltip.position($t); + $div.css(position.home).find('> div').css(_.extend({ + opacity: 0 + }, position.away)); + if ($t.data().tooltipHoverable != null) { + $div.hover(function() { + _.Tooltip.show($t); + return $t.data({ + tooltipHoverableHover: true + }); + }, function() { + $t.data({ + tooltipHoverableHover: false + }); + return _.Tooltip.hide($t); + }); + } else { + $div.css({ + pointerEvents: 'none', + '-webkit-user-select': 'none', + '-moz-user-select': 'none', + userSelect: 'none' + }); + } + } + return $t.data('tooltip$Div'); + }, + show: function($t) { + var $div, o, position; + o = _.Tooltip; + $div = o.divFor($t); + if (!((!($t.data().tooltipNoHover != null) && $t.data().tooltipHover) || (!($t.data().tooltipNoFocus != null) && $t.is(':input:focus')) || $t.data().tooltipHoverableHover)) { + position = o.position($t); + return $div.appendTo($t.parent()).css(_.extend({ + display: 'block' + }, position.home)).find('> div').stop().animate({ + opacity: 1, + top: 0, + left: 0 + }, $t.data().tooltipDuration); + } + }, + hide: function($t) { + var $div, o, position; + o = _.Tooltip; + if ($div = $t.data('tooltip$Div')) { + if (!((!($t.data().tooltipNoHover != null) && $t.data().tooltipHover) || (!($t.data().tooltipNoFocus != null) && $t.is(':input:focus')) || $t.data().tooltipHoverableHover)) { + position = o.position($t); + return $div.css(position.home).find('> div').stop().animate(_.extend({ + opacity: 0 + }, position.away), { + duration: $t.data().tooltipDuration, + complete: function() { + $t.data({ + tooltip$Div: null + }); + return $(this).parent().remove(); + } + }); + } + } + }, + position: function($t) { + var $div, $parent, away, divHeight, divWidth, home, offset, parentScrollLeft, parentScrollTop, tHeight, tLeft, tPosition, tTop, tWidth; + $t = $(this); + $div = $t.data('tooltip$Div'); + $parent = $t.parent(); + offset = $t.data().tooltipOffset; + divWidth = $div.outerWidth(); + divHeight = $div.outerHeight(); + parentScrollLeft = $parent.scrollLeft(); + parentScrollTop = $parent.scrollTop(); + if ($t.data().tooltipMouse != null) { + tLeft = Tooltip.mouse.x - $t.parent().offset().left + parentScrollLeft; + tTop = Tooltip.mouse.y - $t.parent().offset().top + parentScrollTop; + tWidth = tHeight = 0; + } else { + tPosition = $t.position(); + tLeft = tPosition.left + parentScrollLeft + parseInt($t.css('marginLeft')); + tTop = tPosition.top + parentScrollTop + parseInt($t.css('marginTop')); + tWidth = $t.outerWidth(); + tHeight = $t.outerHeight(); + } + home = { + left: tLeft, + top: tTop + }; + away = {}; + switch ($t.data().tooltipPosition) { + case 'top': + home.left += (tWidth - divWidth) / 2; + home.top -= divHeight; + away.top = -offset; + break; + case 'right': + home.left += tWidth; + home.top += (tHeight - divHeight) / 2; + away.left = offset; + break; + case 'bottom': + home.left += (tWidth - divWidth) / 2; + home.top += tHeight; + away.top = offset; + break; + case 'left': + home.left -= divWidth; + home.top += (tHeight - divHeight) / 2; + away.left = -offset; + } + return { + home: home, + away: away + }; + } + }; + + $.extend($.fn, { + tooltip: function(options) { + _.once(Tooltip.bind); + return Tooltip.add($(this), options); + }, + correctTooltip: function() { + return $(this).each(function() { + var $div, $t; + $t = $(this); + $div = $t.data('tooltip$Div'); + if ($div) { + if ($t.css('display') === 'none') { + return $t.mouseleave().blur(); + } else { + $div.find('> div').html($t.data().tooltipHtml); + return $div.css(Tooltip.position($t).home); + } + } + }); + }, + removeTooltip: function() { + return $(this).each(function() { + var $t, _ref; + if ((_ref = ($t = $(this)).data({ + tooltipHover: false, + tooltipHoverableHover: false + }).data().tooltip$Div) != null) { + _ref.remove(); + } + Tooltip.$els = Tooltip.$els.not($t); + return Tooltip.bind(); + }); + } + }); + +}).call(this); diff --git a/dist/konami.js b/dist/konami.js new file mode 100644 index 0000000..c398dcf --- /dev/null +++ b/dist/konami.js @@ -0,0 +1,77 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + + $.Konami = function(callback, options) { + var checkEvents, dX, dY, keyDownEvent, keysPressed, startX, startY, tap, touchEndEvent, touchEvents, touchMoveEvent, touchStartEvent; + if (options == null) { + options = {}; + } + options = _.extend({ + onlyOnce: false, + code: '38,38,40,40,37,39,37,39,66,65,13', + touchCode: 'up,up,down,down,left,right,left,right,tap,tap,tap' + }, options); + keysPressed = []; + touchEvents = []; + tap = false; + startX = startY = dX = dY = 0; + keyDownEvent = function(e) { + keysPressed.push(e.keyCode); + if (_.endsWith(keysPressed + '', options.code)) { + if (options.onlyOnce) { + $(document).off('keydown', keyDownEvent); + } + keysPressed = []; + e.preventDefault(); + return callback(); + } + }; + touchStartEvent = function(e) { + var touch, tracking; + e = e.originalEvent; + if (e.touches.length === 1) { + touch = e.touches[0]; + startX = touch.screenX, startY = touch.screenY; + return tap = tracking = true; + } + }; + touchMoveEvent = function(e) { + var downUp, rightLeft, touch, val; + e = e.originalEvent; + if (e.touches.length === 1 && tap) { + touch = e.touches[0]; + dX = touch.screenX - startX; + dY = touch.screenY - startY; + rightLeft = dX > 0 ? 'right' : 'left'; + downUp = dY > 0 ? 'down' : 'up'; + val = Math.abs(dX) > Math.abs(dY) ? rightLeft : downUp; + touchEvents.push(val); + tap = false; + return checkEvents(e); + } + }; + touchEndEvent = function(e) { + e = e.originalEvent; + if (e.touches.length === 0 && tap) { + touchEvents.push('tap'); + return checkEvents(e); + } + }; + checkEvents = function(e) { + if (_.endsWith(touchEvents + '', options.touchCode)) { + if (options.onlyOnce) { + $(document).off('touchmove', touchMoveEvent); + $(document).off('touchend', touchEndEvent); + } + touchEvents = []; + e.preventDefault(); + return callback(); + } + }; + $(document).on('keydown', keyDownEvent); + $(document).on('touchstart', touchStartEvent); + $(document).on('touchmove', touchMoveEvent); + return $(document).on('touchend', touchEndEvent); + }; + +}).call(this); diff --git a/dist/lazy.js b/dist/lazy.js new file mode 100644 index 0000000..5fe883d --- /dev/null +++ b/dist/lazy.js @@ -0,0 +1,53 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var Lazy; + + Lazy = { + $els: $(), + bind: function() { + return $(window).on('scroll resize', function() { + return Lazy.check(Lazy.$els); + }); + }, + check: function($imgs) { + $imgs.each(function() { + var $t; + $t = $(this); + return _.defer(function() { + var fold, showLine, visible; + visible = _.reduce($t.parents(), function(memo, parent) { + return memo && $(parent).css('display') !== 'none' && $(parent).css('visibility') !== 'hidden'; + }, true); + fold = $(window).scrollTop() + $(window).outerHeight(); + showLine = $t.offset().top - $t.data().lazyTolerance; + if (visible && fold >= showLine) { + $t.attr('src', $t.data().lazySrc); + return Lazy.$els = Lazy.$els.not($t); + } + }); + }); + return $imgs; + } + }; + + Lazy.init = _.once(Lazy.bind); + + $.fn.lazy = function(src, options) { + var $t, tolerance; + if (options == null) { + options = {}; + } + tolerance = options.tolerance; + if (tolerance == null) { + tolerance = 100; + } + $t = $(this); + Lazy.init(); + Lazy.$els = Lazy.$els.add($t.data({ + lazySrc: src, + lazyTolerance: tolerance + })); + return Lazy.check($t); + }; + +}).call(this); diff --git a/dist/placeholder.js b/dist/placeholder.js new file mode 100644 index 0000000..e4bf5b9 --- /dev/null +++ b/dist/placeholder.js @@ -0,0 +1,105 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var Placeholder; + + Placeholder = { + bind: function() { + var val; + val = $.fn.val; + return $.fn.val = function(str) { + var $t; + $t = $(this); + if (str === void 0) { + if ($t.data().placeholderIsEmpty) { + return ''; + } else { + return val.call($t); + } + } else { + if (($t.data().placeholderIsEmpty != null) && !$t.is(':focus')) { + if (str === '' || str === null) { + $t.data({ + placeholderIsEmpty: true + }); + val.call($t, $t.data().placeholderText); + if ($t.data().placeholderIsPassword) { + $t[0].type = 'text'; + } + } else { + val.call($t, str); + if ($t.data().placeholderIsPassword) { + $t[0].type = 'password'; + } + $t.data({ + placeholderIsEmpty: false + }); + } + } else { + val.call($t, str); + } + return $t; + } + }; + }, + listeners: { + focus: function() { + var $t; + $t = $(this); + return _.defer(function() { + if ($t.data().placeholderIsPassword) { + $t[0].type = 'password'; + } + if ($t.data().placeholderIsEmpty) { + $t.val(''); + } + return $t.data({ + placeholderIsEmpty: false + }); + }); + }, + blur: function() { + var $t; + $t = $(this); + return _.defer(function() { + if (!$t.val()) { + return $t.val(''); + } + }); + } + } + }; + + Placeholder.init = _.once(Placeholder.bind); + + $.fn.placeholder = function(text, options) { + var password; + if (options == null) { + options = {}; + } + password = options.password; + Placeholder.init(); + return $(this).each(function() { + var $t; + if (($t = $(this)).data().placeholderIsEmpty == null) { + if (password) { + $t[0].type = 'password'; + } + if (!(password && $.browser.msie && $.browser.version.split('.')[0] < 9)) { + $t.data({ + placeholderText: text, + placeholderIsEmpty: false, + placeholderIsPassword: text + }); + if (!$t.val() || $t.val() === text) { + $t.val(''); + } + return $t.attr({ + placeholder: text, + title: text + }).on(Placeholder.listeners); + } + } + }); + }; + +}).call(this); diff --git a/dist/pop-up.js b/dist/pop-up.js new file mode 100644 index 0000000..acc6865 --- /dev/null +++ b/dist/pop-up.js @@ -0,0 +1,138 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var PopUp, + _this = this; + + PopUp = { + bind: function() { + $('body').append(PopUp.$container = $('
').attr({ + id: 'js-pop-up-container' + }).css({ + display: 'none', + position: 'fixed', + zIndex: 999999, + left: 0, + top: 0, + width: '100%', + height: '100%', + opacity: 0, + overflow: 'auto' + }).append($('
').attr({ + id: 'js-pop-up-table' + }).css({ + display: 'table', + width: '100%', + height: '100%' + }).append($('
').attr({ + id: 'js-pop-up-table-cell' + }).css({ + display: 'table-cell', + textAlign: 'center', + verticalAlign: 'middle' + }).append(PopUp.$div = $('
').attr({ + id: 'js-pop-up' + }).css({ + display: 'inline-block', + position: 'relative' + }))))); + $.PopUp.hide(); + PopUp.$container.on('click', function() { + return PopUp.$div.find('.js-pop-up-outside').click(); + }); + PopUp.$div.on('click', function(e) { + return e.stopPropagation(); + }).on('click', '.js-pop-up-hide', function() { + return $.PopUp.hide; + }); + return $(document).keydown(function(e) { + if (PopUp.$container.css('display') === 'block' && !$('body :focus').length) { + switch (e.keyCode) { + case 13: + PopUp.$div.find('.js-pop-up-enter').click(); + break; + case 27: + PopUp.$div.find('.js-pop-up-esc').click(); + break; + default: + return true; + } + return false; + } + }); + } + }; + + PopUp.init = _.once(PopUp.bind); + + $.PopUp = { + duration: 0, + fadeDuration: 250, + show: function(el, options) { + var $body; + if (options == null) { + options = {}; + } + PopUp.init(); + options = _.extend({ + duration: $.PopUp.duration, + callback: null, + fadeDuration: $.PopUp.fadeDuration + }, options); + $body = $('body'); + if (PopUp.$container.css('display') !== 'block') { + PopUp.bodyStyle = $body.attr('style'); + } + if ($('body').hasScrollbar()) { + $body.css({ + marginRight: $.scrollbarSize() + }); + } + $body.css({ + overflow: 'hidden' + }).find(':focus').blur(); + PopUp.fadeDuration = options.fadeDuration; + PopUp.$div.empty(); + if (typeof el === 'string') { + PopUp.$div.html(el); + } else { + PopUp.$div.append(el); + } + PopUp.$container.stop().css({ + display: 'block' + }).animate({ + opacity: 1 + }, options.fadeDuration); + clearTimeout(PopUp.timeout); + if (options.duration) { + return PopUp.timeout = _.delay(function() { + $.PopUp.hide(); + return typeof options.callback === "function" ? options.callback() : void 0; + }, options.duration); + } + }, + hide: function(fadeDuration) { + if (fadeDuration == null) { + fadeDuration = PopUp.fadeDuration; + } + if (!PopUp.$container) { + return; + } + return PopUp.$container.stop().animate({ + opacity: 0 + }, fadeDuration, function() { + PopUp.$container.css({ + display: 'none' + }); + if (PopUp.bodyStyle) { + $('body').attr({ + style: PopUp.saveBodyStyle + }); + } else { + $('body').removeAttr('style'); + } + return delete PopUp.bodyStyle; + }); + } + }; + +}).call(this); diff --git a/dist/scroll.js b/dist/scroll.js new file mode 100644 index 0000000..554aeb3 --- /dev/null +++ b/dist/scroll.js @@ -0,0 +1,68 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + + $.fn.scrollTo = function(val, options) { + if (val == null) { + val = 0; + } + if (options == null) { + options = {}; + } + if (val instanceof $) { + val = val.offset().top; + } + return $(this).animate({ + scrollTop: val + }, options); + }; + + $.scrollTo = function() { + var $el; + $el = $($.browser.webkit ? document.body : document.documentElement); + return $el.scrollTo.apply($el, arguments); + }; + + $.scrollbarSize = function(dimension) { + var $in, $out, d1, d2; + if (dimension == null) { + dimension = 'width'; + } + $out = $('
').appendTo('body').css({ + position: 'fixed', + overflow: 'hidden', + left: -50, + top: -50, + width: 50, + height: 50 + }); + $in = $out.find('> div').css({ + height: '100%' + }); + d1 = $in[dimension](); + $out.css({ + overflow: 'scroll' + }); + d2 = $in[dimension](); + $out.remove(); + return d1 - d2; + }; + + $.fn.hasScrollbar = function() { + var $t, d1, d2, style; + $t = $(this); + style = $t.attr('style'); + d1 = $t.width(); + d2 = $t.css({ + overflow: 'hidden' + }).width(); + if (style != null) { + $t.attr({ + style: style + }); + } else { + $t.removeAttr('style'); + } + return d1 !== d2; + }; + +}).call(this); diff --git a/dist/search.js b/dist/search.js new file mode 100644 index 0000000..4692b25 --- /dev/null +++ b/dist/search.js @@ -0,0 +1,233 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var Search; + + Search = { + clean: function(str) { + return str.toLowerCase().replace(/\s{2,}/g, ' ').replace(/^\s*|\s*$/g, ''); + }, + page: function($searches, n, prev) { + return $searches.each(function() { + var $results, $search; + $search = $(this); + $results = $search.data('search$Results'); + n = Math.min($results.find('.page').length - 1, Math.max(n, 0)); + $results.find('.selected').removeClass('selected'); + $results.find('.page').css({ + display: 'none' + }).eq(n).removeAttr('style').find('.result:not(.prev):not(.next)')[prev ? 'last' : 'first']().addClass('selected'); + return $search.data('searchPage', n); + }); + }, + query: function($searches, urlN) { + if (urlN == null) { + urlN = 1; + } + return $searches.each(function() { + var $q, $results, $search, anotherSearch, callback, o, q, t, _base, _ref; + o = Search; + $search = $(this); + $results = $search.data('search$Results'); + $q = $search.data('search$Q'); + callback = $search.data().searchCallback; + q = Search.clean($q.val()); + t = new Date().getTime(); + $results.css({ + display: 'block' + }); + anotherSearch = $search.data("searchUrl" + (urlN + 1)) != null; + if (!(q || ($search.data('empty') != null))) { + $results.css({ + display: 'none' + }).empty(); + $search.removeClass('loading'); + } else if (q !== $search.data().searchLastQ || urlN > 1) { + $search.addClass('loading'); + callback($search, null, urlN); + clearTimeout($search.data().searchTimeout); + if (typeof (_base = $search.data().searchAjax).abort === "function") { + _base.abort(); + } + if ($search.data().searchCache[("" + urlN + "_") + q] != null) { + if (!anotherSearch) { + $search.removeClass('loading'); + } + callback($search, $search.data().searchCache[("" + urlN + "_") + q], urlN); + if (anotherSearch) { + o.query($search, urlN + 1); + } + } else { + $search.data('searchTimeout', setTimeout(function() { + var check, handleData; + handleData = function(data) { + $search.data().searchCache[("" + urlN + "_") + q] = data; + if (check === $search.data().searchId && (_.clean($q.val()) || ($search.data('empty') != null))) { + if (!anotherSearch) { + $search.removeClass('loading'); + } + callback($search, data, urlN); + } + if (anotherSearch) { + return o.query($search, urlN + 1); + } + }; + check = $search.data({ + searchId: $search.data().searchId + 1 + }).data().searchId; + if ($search.data().searchJs) { + return handleData($search.data().searchJs(q)); + } else if ($search.data().searchUrl != null) { + return $search.data({ + searchAjax: $.getJSON($search.data("searchUrl" + (urlN === 1 ? '' : urlN)), { + q: q + }, handleData) + }); + } + }, (_ref = $search.data().searchDelay) != null ? _ref : 0)); + } + } + return $search.data('searchLastQ', q); + }); + }, + select: function($searches, dir) { + return $searches.each(function() { + var $page, $search, o; + o = Search; + $search = $(this); + $page = $search.find('.page').eq($search.data().searchPage); + if (!$page.find('.selected').removeClass('selected')[dir]().addClass('selected').length) { + $page.find('.result')[dir === 'prev' ? 'first' : 'last']().addClass('selected'); + } + if ($page.find('.result.selected.prev').length) { + return o.page($search, $search.data().searchPage - 1, true); + } else if ($page.find('.result.selected.next').length) { + return o.page($search, $search.data().searchPage + 1); + } + }); + } + }; + + $.fn.search = function($q, $results, options) { + var $search, key, o, val; + if (options == null) { + options = {}; + } + $search = $(this); + if ($search.data().searchCache == null) { + o = Search; + $search.data({ + searchCache: [], + searchId: 0, + searchAjax: {}, + searchLastQ: null, + searchPage: 0, + searchHoldHover: false, + search$Q: $q, + search$Results: $results + }); + for (key in options) { + val = options[key]; + $search.data("search" + (key.replace(/(\w)/, function(s) { + return s.toUpperCase(); + })), val); + } + if ($q.is(':focus')) { + o.query($search); + } + $search.hover(function() { + return $search.data({ + searchHover: true + }); + }, function() { + $search.data({ + searchHover: false + }); + if (!($q.is(':focus') || $search.data().searchHoldHover)) { + return $results.css({ + display: 'none' + }); + } + }).mouseover(function() { + return $search.data({ + searchHoldHover: false + }); + }); + $q.blur(function() { + return _.defer(function() { + if (!$search.data().searchHover) { + return $results.css({ + display: 'none' + }); + } + }); + }).focus(function() { + return $search.data({ + searchHoldHover: false + }); + }).keydown(function(e) { + switch (e.keyCode) { + case 13: + $search.find('.selected').click(); + break; + case 38: + o.select($search, 'prev'); + break; + case 40: + o.select($search, 'next'); + break; + case 27: + if ($q.val() === '') { + $q.blur(); + _.defer(function() { + return o.query($search); + }); + } else { + _.defer(function() { + $q.val(''); + return o.query($search); + }); + } + break; + default: + _.defer(function() { + if ($q.is(':focus')) { + return o.query($search); + } + }); + return true; + } + return false; + }).on('focus keyup change', function() { + return o.query($search); + }); + return $results.on('mouseenter click', '.result', function(e) { + var $t; + $t = $(this); + $results.find('.result.selected').removeClass('selected'); + $t.addClass('selected'); + if (e.type === 'click') { + if ($t.hasClass('prev')) { + o.page($search, $search.data().searchPage - 1, true); + $search.data({ + searchHoldHover: true + }); + } else if ($t.hasClass('next')) { + o.page($search, $search.data().searchPage + 1); + $search.data({ + searchHoldHover: true + }); + } else if ($t.hasClass('submit')) { + $t.parents('form').submit(); + } + if ($t.hasClass('hide')) { + $q.blur(); + return $results.css({ + display: 'none' + }); + } + } + }); + } + }; + +}).call(this); diff --git a/dist/tooltip.js b/dist/tooltip.js new file mode 100644 index 0000000..a2fe853 --- /dev/null +++ b/dist/tooltip.js @@ -0,0 +1,268 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var Tooltip; + + Tooltip = { + $els: $(), + mouse: { + x: 0, + y: 0 + }, + bind: function() { + return $('body').mousemove(function(e) { + return Tooltip.mouse = { + x: e.pageX, + y: e.pageY + }; + }); + }, + listeners: { + mousemove: function() { + var $t; + if (($t = $(this)).data('tooltip$Div')) { + return $t.data().tooltip$Div.css(Tooltip.position($t).home); + } + }, + mouseenter: function() { + var $t; + Tooltip.show($t = $(this)); + return $t.data({ + tooltipHover: true + }); + }, + mouseleave: function() { + var $t; + ($t = $(this)).data({ + tooltipHover: false + }); + return Tooltip.hide($t); + }, + focus: function() { + return Tooltip.show($(this)); + }, + blur: function() { + return Tooltip.hide($(this)); + } + }, + add: function($els, options) { + var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; + if (options == null) { + options = {}; + } + Tooltip.remove($els); + Tooltip.$els = Tooltip.$els.add($els); + $els.data({ + tooltipHtml: typeof html !== "undefined" && html !== null ? html : '', + tooltipPosition: (_ref = options.position) != null ? _ref : 'top', + tooltipOffset: (_ref1 = options.offset) != null ? _ref1 : 0, + tooltipDuration: (_ref2 = options.duration) != null ? _ref2 : 0, + tooltipNoHover: (_ref3 = options.noHover) != null ? _ref3 : false, + tooltipNoFocus: (_ref4 = options.noFocus) != null ? _ref4 : false, + tooltipMouse: (_ref5 = options.mouse) != null ? _ref5 : false, + tooltipHoverableHover: (_ref6 = options.hoverableHover) != null ? _ref6 : false + }); + if (options.mouse) { + $els.on('mousemove', Tooltip.listeners.mousemove); + } + if (!options.noHover) { + $els.on('mouseenter', Tooltip.listeners.mouseenter); + $els.on('mouseleave', Tooltip.listeners.mouseleave); + } + if (!options.noFocus) { + $els.on('focus', Tooltip.listeners.focus); + $els.on('blur', Tooltip.listeners.blur); + } + return $els; + }, + remove: function($els) { + Tooltip.$els = Tooltip.$els.no($els); + return $els.data({ + tooltipHover: false, + tooltipHoverableHover: false + }).off(Tooltip.listeners).each(function() { + var _ref; + return (_ref = $(this).data().tooltip$Div) != null ? _ref.remove() : void 0; + }); + }, + divFor: function($t) { + var $div, position; + if (!$t.data('tooltip$Div')) { + if ($t.parent().css({ + position: 'static' + })) { + $t.parent().css({ + position: 'relative' + }); + } + if ($t.data().tooltipMouse != null) { + $t.data({ + tooltipHoverable: false + }); + } + $t.data(_.extend({ + tooltipPosition: 'top', + tooltipOffset: 0, + tooltipDuration: 0 + }, $t.data())); + $div = $('
').addClass("tooltip " + ($t.data().tooltipPosition)).css({ + display: 'none', + position: 'absolute', + zIndex: 999999 + }).append($('
').html($t.data().tooltipHtml).css({ + position: 'relative' + })); + $t.data({ + tooltip$Div: $div + }).parent().append($div); + position = Tooltip.position($t); + $div.css(position.home).find('> div').css(_.extend({ + opacity: 0 + }, position.away)); + if ($t.data().tooltipHoverable != null) { + $div.hover(function() { + _.Tooltip.show($t); + return $t.data({ + tooltipHoverableHover: true + }); + }, function() { + $t.data({ + tooltipHoverableHover: false + }); + return _.Tooltip.hide($t); + }); + } else { + $div.css({ + pointerEvents: 'none', + '-webkit-user-select': 'none', + '-moz-user-select': 'none', + userSelect: 'none' + }); + } + } + return $t.data('tooltip$Div'); + }, + show: function($t) { + var $div, o, position; + o = _.Tooltip; + $div = o.divFor($t); + if (!((!($t.data().tooltipNoHover != null) && $t.data().tooltipHover) || (!($t.data().tooltipNoFocus != null) && $t.is(':input:focus')) || $t.data().tooltipHoverableHover)) { + position = o.position($t); + return $div.appendTo($t.parent()).css(_.extend({ + display: 'block' + }, position.home)).find('> div').stop().animate({ + opacity: 1, + top: 0, + left: 0 + }, $t.data().tooltipDuration); + } + }, + hide: function($t) { + var $div, o, position; + o = _.Tooltip; + if ($div = $t.data('tooltip$Div')) { + if (!((!($t.data().tooltipNoHover != null) && $t.data().tooltipHover) || (!($t.data().tooltipNoFocus != null) && $t.is(':input:focus')) || $t.data().tooltipHoverableHover)) { + position = o.position($t); + return $div.css(position.home).find('> div').stop().animate(_.extend({ + opacity: 0 + }, position.away), { + duration: $t.data().tooltipDuration, + complete: function() { + $t.data({ + tooltip$Div: null + }); + return $(this).parent().remove(); + } + }); + } + } + }, + position: function($t) { + var $div, $parent, away, divHeight, divWidth, home, offset, parentScrollLeft, parentScrollTop, tHeight, tLeft, tPosition, tTop, tWidth; + $t = $(this); + $div = $t.data('tooltip$Div'); + $parent = $t.parent(); + offset = $t.data().tooltipOffset; + divWidth = $div.outerWidth(); + divHeight = $div.outerHeight(); + parentScrollLeft = $parent.scrollLeft(); + parentScrollTop = $parent.scrollTop(); + if ($t.data().tooltipMouse != null) { + tLeft = Tooltip.mouse.x - $t.parent().offset().left + parentScrollLeft; + tTop = Tooltip.mouse.y - $t.parent().offset().top + parentScrollTop; + tWidth = tHeight = 0; + } else { + tPosition = $t.position(); + tLeft = tPosition.left + parentScrollLeft + parseInt($t.css('marginLeft')); + tTop = tPosition.top + parentScrollTop + parseInt($t.css('marginTop')); + tWidth = $t.outerWidth(); + tHeight = $t.outerHeight(); + } + home = { + left: tLeft, + top: tTop + }; + away = {}; + switch ($t.data().tooltipPosition) { + case 'top': + home.left += (tWidth - divWidth) / 2; + home.top -= divHeight; + away.top = -offset; + break; + case 'right': + home.left += tWidth; + home.top += (tHeight - divHeight) / 2; + away.left = offset; + break; + case 'bottom': + home.left += (tWidth - divWidth) / 2; + home.top += tHeight; + away.top = offset; + break; + case 'left': + home.left -= divWidth; + home.top += (tHeight - divHeight) / 2; + away.left = -offset; + } + return { + home: home, + away: away + }; + } + }; + + $.extend($.fn, { + tooltip: function(options) { + _.once(Tooltip.bind); + return Tooltip.add($(this), options); + }, + correctTooltip: function() { + return $(this).each(function() { + var $div, $t; + $t = $(this); + $div = $t.data('tooltip$Div'); + if ($div) { + if ($t.css('display') === 'none') { + return $t.mouseleave().blur(); + } else { + $div.find('> div').html($t.data().tooltipHtml); + return $div.css(Tooltip.position($t).home); + } + } + }); + }, + removeTooltip: function() { + return $(this).each(function() { + var $t, _ref; + if ((_ref = ($t = $(this)).data({ + tooltipHover: false, + tooltipHoverableHover: false + }).data().tooltip$Div) != null) { + _ref.remove(); + } + Tooltip.$els = Tooltip.$els.not($t); + return Tooltip.bind(); + }); + } + }); + +}).call(this); diff --git a/lib/backbone-link.coffee b/lib/backbone-link.coffee new file mode 100644 index 0000000..384f7a8 --- /dev/null +++ b/lib/backbone-link.coffee @@ -0,0 +1,51 @@ +$.extend $.fn, + # A backbone UI/model sync'r + backboneLink = (options = {}) -> + {model, attr, save} = options + save ?= true + $(@).each -> + check = $(@).find '[data-backbone-link-attr]' + (if check.length then check else $ @).each -> + $t = $ @ + $t.backboneUnlink() + $t.data backboneLinkModel: model + checkbox = $t.is ':checkbox' + # Define callback functions + $t.data + backboneLinkInputChange: -> + oldVal = model.get attr + newVal = if checkbox then $t.is ':checked' else $t.val() + if attr is 'id' or + _.endsWith(attr, '_id') or + _.endsWith(attr, 'Id') + newVal = if newVal then parseInt newVal else null + model.set attr, if newVal is '' then null else newVal + model.save() if save and newVal isnt oldVal + + $t.data + backboneLinkModelChange: -> + valOrText = if $t.is ':input' then 'val' else 'text' + oldVal = if checkbox then $t.is ':checked' else $t[valOrText]() + newVal = model.get attr + newVal = if newVal is null then '' else newVal + if newVal isnt oldVal + if checkbox + $t.prop checked: not not newVal + else + $t[valOrText] newVal + + # Bind change events + $t.on 'change', $t.data().backboneLinkInputChange if $t.is ':input' + + # Bind and trigger the model change to update the element right now + model.on "change:#{attr}", $t.data().backboneLinkModelChange + $t.data().backboneLinkModelChange() + + # Remove the link to a backbone model + backboneUnlink = -> + $(@).each -> + $t = $ @ + model = $t.data().backboneLinkModel + if model + $t.off 'change', $t.data().backboneLinkInputChange + model.off null, $t.data().backboneLinkModelChange diff --git a/lib/cookie.coffee b/lib/cookie.coffee new file mode 100644 index 0000000..f1f1439 --- /dev/null +++ b/lib/cookie.coffee @@ -0,0 +1,33 @@ +# Making Cookie management easy on you +$.Cookie = (name, val, options = {}) -> + if typeof name is 'object' + $.Cookie n, v, val for n, v of name + $.Cookie() + else if typeof name is 'string' and val isnt undefined + options.expires = -1 if val is null + val ||= '' + params = [] + params.push "; Expires=#{ + if options.expires.toGMTString? + then options.expires.toGMTString() + else new Date(+new Date + options.expires).toGMTString() + }" if options.expires + params.push "; Path=#{options.path}" if options.path + params.push "; Domain=#{options.domain}" if options.domain + params.push '; HttpOnly' if options.httpOnly + params.push '; Secure' if options.secure + encodeName = encodeURIComponent name + encodeVal = encodeURIComponent val + document.cookie = "#{encodeName}=#{encodeVal}#{params.join ''}" + $.Cookie() + else + cookies = {} + if document.cookie + rawCookies = decodeURIComponent(document.cookie).split /\s*;\s*/ + for cookie in rawCookies + {1: n, 2: v} = /^([^=]*)\s*=\s*(.*)$/.exec cookie + if typeof name is 'string' and name is n + return v + else if not name + cookies[n] = v + if not name then cookies else null diff --git a/lib/konami.coffee b/lib/konami.coffee new file mode 100644 index 0000000..abe32e7 --- /dev/null +++ b/lib/konami.coffee @@ -0,0 +1,53 @@ +# Everyone's favorite cheat code +$.Konami = (callback, options = {}) -> + options = _.extend + onlyOnce: false + code: '38,38,40,40,37,39,37,39,66,65,13' + touchCode: 'up,up,down,down,left,right,left,right,tap,tap,tap' + , options + keysPressed = [] + touchEvents = [] + tap = false + startX = startY = dX = dY = 0 + keyDownEvent = (e) -> + keysPressed.push e.keyCode + if _.endsWith keysPressed + '', options.code + $(document).off 'keydown', keyDownEvent if options.onlyOnce + keysPressed = [] + e.preventDefault() + callback() + touchStartEvent = (e) -> + e = e.originalEvent + if e.touches.length is 1 + touch = e.touches[0] + {screenX: startX, screenY: startY} = touch + tap = tracking = true + touchMoveEvent = (e) -> + e = e.originalEvent + if e.touches.length is 1 and tap + touch = e.touches[0] + dX = touch.screenX - startX + dY = touch.screenY - startY + rightLeft = if dX > 0 then 'right' else 'left' + downUp = if dY > 0 then 'down' else 'up' + val = if Math.abs(dX) > Math.abs dY then rightLeft else downUp + touchEvents.push val + tap = false + checkEvents e + touchEndEvent = (e) -> + e = e.originalEvent + if e.touches.length is 0 and tap + touchEvents.push 'tap' + checkEvents e + checkEvents = (e) -> + if _.endsWith touchEvents + '', options.touchCode + if options.onlyOnce + $(document).off 'touchmove', touchMoveEvent + $(document).off 'touchend', touchEndEvent + touchEvents = [] + e.preventDefault() + callback() + $(document).on 'keydown', keyDownEvent + $(document).on 'touchstart', touchStartEvent + $(document).on 'touchmove', touchMoveEvent + $(document).on 'touchend', touchEndEvent diff --git a/lib/lazy.coffee b/lib/lazy.coffee new file mode 100644 index 0000000..4e3e5d1 --- /dev/null +++ b/lib/lazy.coffee @@ -0,0 +1,35 @@ +# Load it right before you see it +Lazy = + $els: $() + + bind: -> + $(window).on 'scroll resize', -> + Lazy.check Lazy.$els + + check: ($imgs) -> + $imgs.each -> + $t = $ @ + _.defer -> + visible = _.reduce $t.parents(), (memo, parent) -> + memo and $(parent).css('display') isnt 'none' and + $(parent).css('visibility') isnt 'hidden' + , true + fold = $(window).scrollTop() + $(window).outerHeight() + showLine = $t.offset().top - $t.data().lazyTolerance + if visible and fold >= showLine + $t.attr 'src', $t.data().lazySrc + Lazy.$els = Lazy.$els.not $t + $imgs + +Lazy.init = _.once Lazy.bind + +# Load images only when they're on the page or about to be on it +$.fn.lazy = (src, options = {}) -> + {tolerance} = options + tolerance ?= 100 + $t = $ @ + Lazy.init() + Lazy.$els = Lazy.$els.add $t.data + lazySrc: src + lazyTolerance: tolerance + Lazy.check $t diff --git a/lib/placeholder.coffee b/lib/placeholder.coffee new file mode 100644 index 0000000..be6c3ce --- /dev/null +++ b/lib/placeholder.coffee @@ -0,0 +1,62 @@ +Placeholder = + + # Hijack jQuery's .val() so it will return an empty string if placeholder + # says it should + bind: -> + val = $.fn.val + $.fn.val = (str) -> + $t = $ @ + if str is undefined + if $t.data().placeholderIsEmpty then '' else val.call $t + else + if $t.data().placeholderIsEmpty? and not $t.is ':focus' + if str in ['', null] + $t.data placeholderIsEmpty: true + val.call $t, $t.data().placeholderText + $t[0].type = 'text' if $t.data().placeholderIsPassword + else + val.call $t, str + $t[0].type = 'password' if $t.data().placeholderIsPassword + $t.data placeholderIsEmpty: false + else + val.call $t, str + $t + + listeners: + focus: -> + $t = $ @ + _.defer -> + $t[0].type = 'password' if $t.data().placeholderIsPassword + $t.val '' if $t.data().placeholderIsEmpty + $t.data placeholderIsEmpty: false + + blur: -> + $t = $ @ + _.defer -> + $t.val '' unless $t.val() + +Placeholder.init = _.once Placeholder.bind + +# Expose to jQuery +$.fn.placeholder = (text, options = {}) -> + {password} = options + + # Run the highjack function if it hasn't been run yet + Placeholder.init() + + $(@).each -> + unless ($t = $ @).data().placeholderIsEmpty? + $t[0].type = 'password' if password + unless password and $.browser.msie and + $.browser.version.split('.')[0] < 9 + $t.data + placeholderText: text + placeholderIsEmpty: false + placeholderIsPassword: text + $t.val '' if not $t.val() or $t.val() is text + $t.attr( + placeholder: text + title: text + ) + .on Placeholder.listeners + diff --git a/lib/pop-up.coffee b/lib/pop-up.coffee new file mode 100644 index 0000000..3a147f3 --- /dev/null +++ b/lib/pop-up.coffee @@ -0,0 +1,121 @@ +# Multipurpose PopUp +PopUp = + + # Build the PopUp element + bind: => + + # Until 'display: box' becomes more widely available, we're stuck with + # table/table-cell + $('body').append PopUp.$container = $('
') + .attr(id: 'js-pop-up-container') + .css( + display: 'none' + position: 'fixed' + zIndex: 999999 + left: 0 + top: 0 + width: '100%' + height: '100%' + opacity: 0 + overflow: 'auto' + ) + .append $('
') + .attr(id: 'js-pop-up-table') + .css( + display: 'table' + width: '100%' + height: '100%' + ) + .append $('
') + .attr(id: 'js-pop-up-table-cell') + .css( + display: 'table-cell' + textAlign: 'center' + verticalAlign: 'middle' + ) + .append PopUp.$div = $('
') + .attr(id: 'js-pop-up') + .css + display: 'inline-block' + position: 'relative' + $.PopUp.hide() + PopUp.$container.on 'click', -> + PopUp.$div.find('.js-pop-up-outside').click() + PopUp.$div + .on('click', (e) -> e.stopPropagation()) + .on 'click', '.js-pop-up-hide', -> $.PopUp.hide + $(document).keydown (e) -> + if PopUp.$container.css('display') is 'block' and + not $('body :focus').length + switch e.keyCode + when 13 then PopUp.$div.find('.js-pop-up-enter').click() + when 27 then PopUp.$div.find('.js-pop-up-esc').click() + else return true + false + +PopUp.init = _.once PopUp.bind + +# Expose to jQuery +$.PopUp = + + # Duration and Fade duration default, feel free to override + duration: 0 + fadeDuration: 250 + + # Show the PopUp with the given `el`, optionally for a 'duration', with a + # 'callback', and/or with a 'fadeDuration' + show: (el, options = {}) -> + + # Build the PopUp element once + PopUp.init() + + # Default options + options = _.extend + duration: $.PopUp.duration + callback: null + fadeDuration: $.PopUp.fadeDuration + , options + + $body = $ 'body' + unless PopUp.$container.css('display') is 'block' + PopUp.bodyStyle = $body.attr 'style' + $body.css marginRight: $.scrollbarSize() if $('body').hasScrollbar() + $body + .css(overflow: 'hidden') + .find(':focus').blur() + + PopUp.fadeDuration = options.fadeDuration + PopUp.$div.empty() + if typeof el is 'string' + PopUp.$div.html el + else + PopUp.$div.append el + + PopUp.$container + .stop() + .css(display: 'block') + .animate + opacity: 1 + , options.fadeDuration + + clearTimeout PopUp.timeout + PopUp.timeout = _.delay -> + $.PopUp.hide() + options.callback?() + , options.duration if options.duration + + # Fade the PopUp out + hide: (fadeDuration = PopUp.fadeDuration) -> + return unless PopUp.$container + PopUp.$container + .stop() + .animate + opacity: 0 + , fadeDuration + , -> + PopUp.$container.css display: 'none' + if PopUp.bodyStyle + $('body').attr style: PopUp.saveBodyStyle + else + $('body').removeAttr 'style' + delete PopUp.bodyStyle diff --git a/lib/scroll.coffee b/lib/scroll.coffee new file mode 100644 index 0000000..1c38d89 --- /dev/null +++ b/lib/scroll.coffee @@ -0,0 +1,40 @@ +# A quick zip to the top of the page, or optionally to an integer or +# jQuery object specified by `val` +$.fn.scrollTo = (val = 0, options = {}) -> + val = val.offset().top if val instanceof $ + $(@).animate scrollTop: val, options + +# Static version that defaults to the body/document (browser specific) +$.scrollTo = -> + $el = $ if $.browser.webkit then document.body else document.documentElement + $el.scrollTo arguments... + +# Sometimes it's handy to know the size of the scrollbars in a browser +$.scrollbarSize = (dimension = 'width') -> + $out = $('
') + .appendTo('body') + .css + position: 'fixed' + overflow: 'hidden' + left: -50 + top: -50 + width: 50 + height: 50 + $in = $out.find('> div').css height: '100%' + d1 = $in[dimension]() + $out.css overflow: 'scroll' + d2 = $in[dimension]() + $out.remove() + d1-d2 + +# Does the element have a scrollbar? +$.fn.hasScrollbar = -> + $t = $ @ + style = $t.attr 'style' + d1 = $t.width() + d2 = $t.css(overflow: 'hidden').width() + if style? + $t.attr style: style + else + $t.removeAttr 'style' + d1 isnt d2 diff --git a/lib/search.coffee b/lib/search.coffee new file mode 100644 index 0000000..63ee207 --- /dev/null +++ b/lib/search.coffee @@ -0,0 +1,146 @@ +# Search (as you type) +Search = + + # Dirty string? Clean it up! + clean: (str) -> + str.toLowerCase().replace(/\s{2,}/g, ' ').replace /^\s*|\s*$/g, '' + + # Change the current search results page + page: ($searches, n, prev) -> + $searches.each -> + $search = $ @ + $results = $search.data 'search$Results' + n = Math.min $results.find('.page').length - 1, Math.max n, 0 + $results.find('.selected').removeClass 'selected' + $results.find('.page') + .css(display: 'none') + .eq(n) + .removeAttr('style') + .find('.result:not(.prev):not(.next)')[if prev then 'last' else 'first']() + .addClass 'selected' + $search.data 'searchPage', n + + # Send the value of q to the correct search function and return the result to the correct callback + query: ($searches, urlN = 1) -> + $searches.each -> + o = Search + $search = $ @ + $results = $search.data 'search$Results' + $q = $search.data 'search$Q' + callback = $search.data().searchCallback + q = Search.clean $q.val() + t = new Date().getTime() + $results.css display: 'block' + anotherSearch = $search.data("searchUrl#{urlN + 1}")? + unless q or $search.data('empty')? + $results.css(display: 'none').empty() + $search.removeClass 'loading' + else if q isnt $search.data().searchLastQ or urlN > 1 + $search.addClass 'loading' + callback $search, null, urlN + clearTimeout $search.data().searchTimeout + $search.data().searchAjax.abort?() + if $search.data().searchCache["#{urlN}_" + q]? + $search.removeClass 'loading' unless anotherSearch + callback $search, $search.data().searchCache["#{urlN}_" + q], urlN + o.query $search, urlN + 1 if anotherSearch + else + $search.data 'searchTimeout', + setTimeout -> + handleData = (data) -> + $search.data().searchCache["#{urlN}_" + q] = data + if check is $search.data().searchId and (_.clean($q.val()) or $search.data('empty')?) + $search.removeClass 'loading' unless anotherSearch + callback $search, data, urlN + o.query $search, urlN + 1 if anotherSearch + check = $search.data(searchId: $search.data().searchId + 1).data().searchId + if $search.data().searchJs + handleData $search.data().searchJs q + else if $search.data().searchUrl? + $search.data searchAjax: $.getJSON($search.data("searchUrl#{if urlN is 1 then '' else urlN}"), q: q, handleData) + , $search.data().searchDelay ? 0 + $search.data 'searchLastQ', q + + # Select the next or previous in a list of results + select: ($searches, dir) -> + $searches.each -> + o = Search + $search = $ @ + $page = $search.find('.page').eq $search.data().searchPage + unless $page.find('.selected').removeClass('selected')[dir]().addClass('selected').length + $page.find('.result')[if dir is 'prev' then 'first' else 'last']().addClass 'selected' + if $page.find('.result.selected.prev').length + o.page $search, $search.data().searchPage - 1, true + else if $page.find('.result.selected.next').length + o.page $search, $search.data().searchPage + 1 + +# Expose to jQuery +$.fn.search = ($q, $results, options = {}) -> + $search = $ @ + unless $search.data().searchCache? + o = Search + $search.data + searchCache: [] + searchId: 0 + searchAjax: {} + searchLastQ: null + searchPage: 0 + searchHoldHover: false + search$Q: $q + search$Results: $results + + for key, val of options + $search.data "search#{key.replace /(\w)/, (s) -> s.toUpperCase()}", val + + o.query $search if $q.is ':focus' + $search + .hover(-> + $search.data searchHover: true + , -> + $search.data searchHover: false + $results.css display: 'none' unless $q.is(':focus') or + $search.data().searchHoldHover + ) + .mouseover -> + $search.data searchHoldHover: false + + $q + .blur(-> _.defer -> $results.css display: 'none' unless $search.data().searchHover) + .focus(-> $search.data searchHoldHover: false) + .keydown((e) -> + switch e.keyCode + when 13 then $search.find('.selected').click() + when 38 then o.select $search, 'prev' + when 40 then o.select $search, 'next' + when 27 + if $q.val() is '' + $q.blur() + _.defer -> + o.query $search + else + _.defer -> + $q.val '' + o.query $search + else + _.defer -> o.query $search if $q.is ':focus' + return true + false + ) + .on 'focus keyup change', -> o.query $search + + $results.on 'mouseenter click', '.result', (e) -> + $t = $ @ + $results.find('.result.selected').removeClass 'selected' + $t.addClass 'selected' + if e.type is 'click' + if $t.hasClass 'prev' + o.page $search, $search.data().searchPage - 1, true + $search.data searchHoldHover: true + else if $t.hasClass 'next' + o.page $search, $search.data().searchPage + 1 + $search.data searchHoldHover: true + else if $t.hasClass 'submit' + $t.parents('form').submit() + if $t.hasClass 'hide' + $q.blur() + $results.css display: 'none' diff --git a/lib/tooltip.coffee b/lib/tooltip.coffee new file mode 100644 index 0000000..ca5c796 --- /dev/null +++ b/lib/tooltip.coffee @@ -0,0 +1,229 @@ +# Yay tooltips! +Tooltip = + + # Keep track of the elements we want to update + $els: $() + + # Store mouse coordinates + mouse: + x: 0 + y: 0 + + # Set events on document to track changes + bind: -> + $('body').mousemove (e) -> + Tooltip.mouse = + x: e.pageX + y: e.pageY + + listeners: + mousemove: -> + if ($t = $ @).data 'tooltip$Div' + $t.data().tooltip$Div.css Tooltip.position($t).home + + mouseenter: -> + Tooltip.show $t = $ @ + $t.data tooltipHover: true + + mouseleave: -> + ($t = $ @).data tooltipHover: false + Tooltip.hide $t + + focus: -> Tooltip.show $ @ + + blur: -> Tooltip.hide $ @ + + add: ($els, options = {}) -> + Tooltip.remove $els + Tooltip.$els = Tooltip.$els.add $els + $els.data + tooltipHtml: html ? '' + tooltipPosition: options.position ? 'top' + tooltipOffset: options.offset ? 0 + tooltipDuration: options.duration ? 0 + tooltipNoHover: options.noHover ? false + tooltipNoFocus: options.noFocus ? false + tooltipMouse: options.mouse ? false + tooltipHoverableHover: options.hoverableHover ? false + $els.on 'mousemove', Tooltip.listeners.mousemove if options.mouse + unless options.noHover + $els.on 'mouseenter', Tooltip.listeners.mouseenter + $els.on 'mouseleave', Tooltip.listeners.mouseleave + unless options.noFocus + $els.on 'focus', Tooltip.listeners.focus + $els.on 'blur', Tooltip.listeners.blur + $els + + remove: ($els) -> + Tooltip.$els = Tooltip.$els.no $els + $els + .data( + tooltipHover: false + tooltipHoverableHover: false + ) + .off(Tooltip.listeners) + .each -> + $(@).data().tooltip$Div?.remove() + + # Get the current tooltip$Div for an item or create a new one and return that + divFor: ($t) -> + unless $t.data 'tooltip$Div' + $t.parent().css position: 'relative' if $t.parent().css position: 'static' + $t.data tooltipHoverable: false if $t.data().tooltipMouse? + $t.data _.extend + tooltipPosition: 'top' + tooltipOffset: 0 + tooltipDuration: 0 + , $t.data() + $div = $('
') + .addClass("tooltip #{$t.data().tooltipPosition}") + .css( + display: 'none' + position: 'absolute' + zIndex: 999999; + ) + .append $('
') + .html($t.data().tooltipHtml) + .css + position: 'relative' + $t + .data(tooltip$Div: $div) + .parent() + .append $div + position = Tooltip.position $t + $div + .css(position.home) + .find('> div') + .css _.extend {opacity: 0}, position.away + + # If the tooltip is 'hoverable' (aka it should stay while the mouse is + # over the tooltip itself) + if $t.data().tooltipHoverable? + $div.hover -> + _.Tooltip.show $t + $t.data tooltipHoverableHover: true + , -> + $t.data tooltipHoverableHover: false + _.Tooltip.hide $t + else + + # Otherwise turn off interaction with the mouse + $div.css + pointerEvents: 'none' + '-webkit-user-select': 'none' + '-moz-user-select': 'none' + userSelect: 'none' + + # Finally, return the div + $t.data 'tooltip$Div' + + # Show the tooltip if it's not already visible + show: ($t) -> + o = _.Tooltip + $div = o.divFor $t + unless (not $t.data().tooltipNoHover? and $t.data().tooltipHover) or + (not $t.data().tooltipNoFocus? and $t.is ':input:focus') or + $t.data().tooltipHoverableHover + position = o.position $t + $div + .appendTo($t.parent()) + .css(_.extend {display: 'block'}, position.home) + .find('> div') + .stop() + .animate + opacity: 1 + top: 0 + left: 0 + , $t.data().tooltipDuration + + # Hide the tooltip if it's not already hidden + hide: ($t) -> + o = _.Tooltip + if $div = $t.data 'tooltip$Div' + unless (not $t.data().tooltipNoHover? and $t.data().tooltipHover) or + (not $t.data().tooltipNoFocus? and $t.is ':input:focus') or + $t.data().tooltipHoverableHover + position = o.position $t + $div + .css(position.home) + .find('> div') + .stop() + .animate _.extend({opacity: 0}, position.away), + duration: $t.data().tooltipDuration + complete: -> + $t.data tooltip$Div: null + $(@).parent().remove() + + # Method for getting the correct CSS position data for a tooltip + position: ($t) -> + $t = $ @ + $div = $t.data 'tooltip$Div' + $parent = $t.parent() + offset = $t.data().tooltipOffset + divWidth = $div.outerWidth() + divHeight = $div.outerHeight() + parentScrollLeft = $parent.scrollLeft() + parentScrollTop = $parent.scrollTop() + if $t.data().tooltipMouse? + tLeft = Tooltip.mouse.x - $t.parent().offset().left + parentScrollLeft + tTop = Tooltip.mouse.y - $t.parent().offset().top + parentScrollTop + tWidth = tHeight = 0 + else + tPosition = $t.position() + tLeft = tPosition.left + parentScrollLeft + parseInt $t.css 'marginLeft' + tTop = tPosition.top + parentScrollTop + parseInt $t.css 'marginTop' + tWidth = $t.outerWidth() + tHeight = $t.outerHeight() + home = + left: tLeft + top: tTop + away = {} + switch $t.data().tooltipPosition + when 'top' + home.left += (tWidth - divWidth)/2 + home.top -= divHeight + away.top = -offset + when 'right' + home.left += tWidth + home.top += (tHeight - divHeight)/2 + away.left = offset + when 'bottom' + home.left += (tWidth - divWidth)/2 + home.top += tHeight + away.top = offset + when 'left' + home.left -= divWidth + home.top += (tHeight - divHeight)/2 + away.left = -offset + {home, away} + +# Expose to jQuery +$.extend $.fn, + tooltip: (options) -> + _.once Tooltip.bind + Tooltip.add $(@), options + + # Use this to correct the tooltip content and positioning between events if + # necessary + correctTooltip: -> + $(@).each -> + $t = $ @ + $div = $t.data 'tooltip$Div' + if $div + if $t.css('display') is 'none' + $t.mouseleave().blur() + else + $div.find('> div').html($t.data().tooltipHtml) + $div.css Tooltip.position($t).home + + # Use this to remove a tooltip + removeTooltip: -> + $(@).each -> + ($t = $ @) + .data( + tooltipHover: false + tooltipHoverableHover: false + ) + .data().tooltip$Div?.remove() + Tooltip.$els = Tooltip.$els.not $t + Tooltip.bind() diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c80844 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "jquery-cwd", + "version": "0.0.1", + "author": "Casey Foster ", + "licence": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/caseywebdev/jquery-cwd.git" + }, + "devDependencies": { + "coffee-script": "x", + "mocha": "x", + "chai": "x" + }, + "engines": { + "node": "x", + "npm": "x" + } +} diff --git a/test/test.coffee b/test/test.coffee new file mode 100644 index 0000000..f91c241 --- /dev/null +++ b/test/test.coffee @@ -0,0 +1,6 @@ +mocha.setup 'bdd' +should = chai.should() + +# TODO + +mocha.run() diff --git a/test/test.html b/test/test.html new file mode 100644 index 0000000..b392d61 --- /dev/null +++ b/test/test.html @@ -0,0 +1,19 @@ + + + + + + + dpr test + + + + + + + +
+ + + + diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..48cb13f --- /dev/null +++ b/test/test.js @@ -0,0 +1,11 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + var should; + + mocha.setup('bdd'); + + should = chai.should(); + + mocha.run(); + +}).call(this);